]> git.ozlabs.org Git - ppp.git/blob - pppd/chap_ms.c
Reduce the clutter by defining the MD4/MD5/SHA digest lengths in one place. Avoid...
[ppp.git] / pppd / chap_ms.c
1 /*
2  * chap_ms.c - Microsoft MS-CHAP compatible implementation.
3  *
4  * Copyright (c) 1995 Eric Rosenquist.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * 3. The name(s) of the authors of this software must not be used to
19  *    endorse or promote products derived from this software without
20  *    prior written permission.
21  *
22  * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
23  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
24  * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
25  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
26  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
27  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
28  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
29  */
30
31 /*
32  * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
33  *
34  *   Implemented LANManager type password response to MS-CHAP challenges.
35  *   Now pppd provides both NT style and LANMan style blocks, and the
36  *   prefered is set by option "ms-lanman". Default is to use NT.
37  *   The hash text (StdText) was taken from Win95 RASAPI32.DLL.
38  *
39  *   You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
40  */
41
42 /*
43  * Modifications by Frank Cusack, frank@google.com, March 2002.
44  *
45  *   Implemented MS-CHAPv2 functionality, heavily based on sample
46  *   implementation in RFC 2759.  Implemented MPPE functionality,
47  *   heavily based on sample implementation in RFC 3079.
48  *
49  * Copyright (c) 2002 Google, Inc.  All rights reserved.
50  *
51  * Redistribution and use in source and binary forms, with or without
52  * modification, are permitted provided that the following conditions
53  * are met:
54  *
55  * 1. Redistributions of source code must retain the above copyright
56  *    notice, this list of conditions and the following disclaimer.
57  *
58  * 2. Redistributions in binary form must reproduce the above copyright
59  *    notice, this list of conditions and the following disclaimer in
60  *    the documentation and/or other materials provided with the
61  *    distribution.
62  *
63  * 3. The name(s) of the authors of this software must not be used to
64  *    endorse or promote products derived from this software without
65  *    prior written permission.
66  *
67  * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
68  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
69  * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
70  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
71  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
72  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
73  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
74  *
75  */
76
77 #define RCSID   "$Id: chap_ms.c,v 1.38 2007/12/01 20:10:51 carlsonj Exp $"
78
79 #ifdef HAVE_CONFIG_H
80 #include "config.h"
81 #endif
82
83 #include <stdio.h>
84 #include <stdlib.h>
85 #include <string.h>
86 #include <ctype.h>
87 #include <sys/types.h>
88 #include <sys/time.h>
89 #include <unistd.h>
90 #if defined(SOL2)
91 #include <net/ppp-comp.h>
92 #else
93 #include <linux/ppp-comp.h>
94 #endif
95
96 #include "pppd.h"
97 #include "chap-new.h"
98 #include "chap_ms.h"
99 #include "magic.h"
100 #include "mppe.h"
101 #include "ppp-crypto.h"
102
103 #ifdef UNIT_TEST
104 #undef PPP_WITH_MPPE
105 #endif
106
107 static void     ascii2unicode (char[], int, u_char[]);
108 static void     NTPasswordHash (u_char *, int, unsigned char *);
109 static int      ChallengeResponse (u_char *, u_char *, u_char*);
110 static void     ChapMS_NT (u_char *, char *, int, u_char[24]);
111 static void     ChapMS2_NT (u_char *, u_char[16], char *, char *, int,
112                                 u_char[24]);
113 static void     GenerateAuthenticatorResponsePlain
114                         (char*, int, u_char[24], u_char[16], u_char *,
115                          char *, u_char[41]);
116 #ifdef PPP_WITH_MSLANMAN
117 static void     ChapMS_LANMan (u_char *, char *, int, u_char *);
118 #endif
119
120 #ifdef PPP_WITH_MSLANMAN
121 bool    ms_lanman = 0;          /* Use LanMan password instead of NT */
122                                 /* Has meaning only with MS-CHAP challenges */
123 #endif
124
125 #ifdef PPP_WITH_MPPE
126 #ifdef DEBUGMPPEKEY
127 /* For MPPE debug */
128 /* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */
129 static char *mschap_challenge = NULL;
130 /* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */
131 static char *mschap2_peer_challenge = NULL;
132 #endif
133
134 #include "fsm.h"                /* Need to poke MPPE options */
135 #include "ccp.h"
136 #endif
137
138 /*
139  * Command-line options.
140  */
141 static option_t chapms_option_list[] = {
142 #ifdef PPP_WITH_MSLANMAN
143         { "ms-lanman", o_bool, &ms_lanman,
144           "Use LanMan passwd when using MS-CHAP", 1 },
145 #endif
146 #ifdef DEBUGMPPEKEY
147         { "mschap-challenge", o_string, &mschap_challenge,
148           "specify CHAP challenge" },
149         { "mschap2-peer-challenge", o_string, &mschap2_peer_challenge,
150           "specify CHAP peer challenge" },
151 #endif
152         { NULL }
153 };
154
155 /*
156  * chapms_generate_challenge - generate a challenge for MS-CHAP.
157  * For MS-CHAP the challenge length is fixed at 8 bytes.
158  * The length goes in challenge[0] and the actual challenge starts
159  * at challenge[1].
160  */
161 static void
162 chapms_generate_challenge(unsigned char *challenge)
163 {
164         *challenge++ = 8;
165 #ifdef DEBUGMPPEKEY
166         if (mschap_challenge && strlen(mschap_challenge) == 8)
167                 memcpy(challenge, mschap_challenge, 8);
168         else
169 #endif
170                 random_bytes(challenge, 8);
171 }
172
173 static void
174 chapms2_generate_challenge(unsigned char *challenge)
175 {
176         *challenge++ = 16;
177 #ifdef DEBUGMPPEKEY
178         if (mschap_challenge && strlen(mschap_challenge) == 16)
179                 memcpy(challenge, mschap_challenge, 16);
180         else
181 #endif
182                 random_bytes(challenge, 16);
183 }
184
185 static int
186 chapms_verify_response(int id, char *name,
187                        unsigned char *secret, int secret_len,
188                        unsigned char *challenge, unsigned char *response,
189                        char *message, int message_space)
190 {
191         unsigned char md[MS_CHAP_RESPONSE_LEN];
192         int diff;
193         int challenge_len, response_len;
194
195         challenge_len = *challenge++;   /* skip length, is 8 */
196         response_len = *response++;
197         if (response_len != MS_CHAP_RESPONSE_LEN)
198                 goto bad;
199
200 #ifndef PPP_WITH_MSLANMAN
201         if (!response[MS_CHAP_USENT]) {
202                 /* Should really propagate this into the error packet. */
203                 notice("Peer request for LANMAN auth not supported");
204                 goto bad;
205         }
206 #endif
207
208         /* Generate the expected response. */
209         ChapMS(challenge, (char *)secret, secret_len, md);
210
211 #ifdef PPP_WITH_MSLANMAN
212         /* Determine which part of response to verify against */
213         if (!response[MS_CHAP_USENT])
214                 diff = memcmp(&response[MS_CHAP_LANMANRESP],
215                               &md[MS_CHAP_LANMANRESP], MS_CHAP_LANMANRESP_LEN);
216         else
217 #endif
218                 diff = memcmp(&response[MS_CHAP_NTRESP], &md[MS_CHAP_NTRESP],
219                               MS_CHAP_NTRESP_LEN);
220
221         if (diff == 0) {
222                 slprintf(message, message_space, "Access granted");
223                 return 1;
224         }
225
226  bad:
227         /* See comments below for MS-CHAP V2 */
228         slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
229                  challenge_len, challenge);
230         return 0;
231 }
232
233 static int
234 chapms2_verify_response(int id, char *name,
235                         unsigned char *secret, int secret_len,
236                         unsigned char *challenge, unsigned char *response,
237                         char *message, int message_space)
238 {
239         unsigned char md[MS_CHAP2_RESPONSE_LEN];
240         char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
241         int challenge_len, response_len;
242
243         challenge_len = *challenge++;   /* skip length, is 16 */
244         response_len = *response++;
245         if (response_len != MS_CHAP2_RESPONSE_LEN)
246                 goto bad;       /* not even the right length */
247
248         /* Generate the expected response and our mutual auth. */
249         ChapMS2(challenge, &response[MS_CHAP2_PEER_CHALLENGE], name,
250                 (char *)secret, secret_len, md,
251                 (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR);
252
253         /* compare MDs and send the appropriate status */
254         /*
255          * Per RFC 2759, success message must be formatted as
256          *     "S=<auth_string> M=<message>"
257          * where
258          *     <auth_string> is the Authenticator Response (mutual auth)
259          *     <message> is a text message
260          *
261          * However, some versions of Windows (win98 tested) do not know
262          * about the M=<message> part (required per RFC 2759) and flag
263          * it as an error (reported incorrectly as an encryption error
264          * to the user).  Since the RFC requires it, and it can be
265          * useful information, we supply it if the peer is a conforming
266          * system.  Luckily (?), win98 sets the Flags field to 0x04
267          * (contrary to RFC requirements) so we can use that to
268          * distinguish between conforming and non-conforming systems.
269          *
270          * Special thanks to Alex Swiridov <say@real.kharkov.ua> for
271          * help debugging this.
272          */
273         if (memcmp(&md[MS_CHAP2_NTRESP], &response[MS_CHAP2_NTRESP],
274                    MS_CHAP2_NTRESP_LEN) == 0) {
275                 if (response[MS_CHAP2_FLAGS])
276                         slprintf(message, message_space, "S=%s", saresponse);
277                 else
278                         slprintf(message, message_space, "S=%s M=%s",
279                                  saresponse, "Access granted");
280                 return 1;
281         }
282
283  bad:
284         /*
285          * Failure message must be formatted as
286          *     "E=e R=r C=c V=v M=m"
287          * where
288          *     e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE)
289          *     r = retry (we use 1, ok to retry)
290          *     c = challenge to use for next response, we reuse previous
291          *     v = Change Password version supported, we use 0
292          *     m = text message
293          *
294          * The M=m part is only for MS-CHAPv2.  Neither win2k nor
295          * win98 (others untested) display the message to the user anyway.
296          * They also both ignore the E=e code.
297          *
298          * Note that it's safe to reuse the same challenge as we don't
299          * actually accept another response based on the error message
300          * (and no clients try to resend a response anyway).
301          *
302          * Basically, this whole bit is useless code, even the small
303          * implementation here is only because of overspecification.
304          */
305         slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
306                  challenge_len, challenge, "Access denied");
307         return 0;
308 }
309
310 static void
311 chapms_make_response(unsigned char *response, int id, char *our_name,
312                      unsigned char *challenge, char *secret, int secret_len,
313                      unsigned char *private)
314 {
315         challenge++;    /* skip length, should be 8 */
316         *response++ = MS_CHAP_RESPONSE_LEN;
317         ChapMS(challenge, secret, secret_len, response);
318 }
319
320 struct chapms2_response_cache_entry {
321         int id;
322         unsigned char challenge[16];
323         unsigned char response[MS_CHAP2_RESPONSE_LEN];
324         unsigned char auth_response[MS_AUTH_RESPONSE_LENGTH];
325 };
326
327 #define CHAPMS2_MAX_RESPONSE_CACHE_SIZE 10
328 static struct chapms2_response_cache_entry
329     chapms2_response_cache[CHAPMS2_MAX_RESPONSE_CACHE_SIZE];
330 static int chapms2_response_cache_next_index = 0;
331 static int chapms2_response_cache_size = 0;
332
333 static void
334 chapms2_add_to_response_cache(int id, unsigned char *challenge,
335                               unsigned char *response,
336                               unsigned char *auth_response)
337 {
338         int i = chapms2_response_cache_next_index;
339
340         chapms2_response_cache[i].id = id;
341         memcpy(chapms2_response_cache[i].challenge, challenge, 16);
342         memcpy(chapms2_response_cache[i].response, response,
343                MS_CHAP2_RESPONSE_LEN);
344         memcpy(chapms2_response_cache[i].auth_response,
345                auth_response, MS_AUTH_RESPONSE_LENGTH);
346         chapms2_response_cache_next_index =
347                 (i + 1) % CHAPMS2_MAX_RESPONSE_CACHE_SIZE;
348         if (chapms2_response_cache_next_index > chapms2_response_cache_size)
349                 chapms2_response_cache_size = chapms2_response_cache_next_index;
350         dbglog("added response cache entry %d", i);
351 }
352
353 static struct chapms2_response_cache_entry*
354 chapms2_find_in_response_cache(int id, unsigned char *challenge,
355                       unsigned char *auth_response)
356 {
357         int i;
358
359         for (i = 0; i < chapms2_response_cache_size; i++) {
360                 if (id == chapms2_response_cache[i].id
361                     && (!challenge
362                         || memcmp(challenge,
363                                   chapms2_response_cache[i].challenge,
364                                   16) == 0)
365                     && (!auth_response
366                         || memcmp(auth_response,
367                                   chapms2_response_cache[i].auth_response,
368                                   MS_AUTH_RESPONSE_LENGTH) == 0)) {
369                         dbglog("response found in cache (entry %d)", i);
370                         return &chapms2_response_cache[i];
371                 }
372         }
373         return NULL;  /* not found */
374 }
375
376 static void
377 chapms2_make_response(unsigned char *response, int id, char *our_name,
378                       unsigned char *challenge, char *secret, int secret_len,
379                       unsigned char *private)
380 {
381         const struct chapms2_response_cache_entry *cache_entry;
382         unsigned char auth_response[MS_AUTH_RESPONSE_LENGTH+1];
383
384         challenge++;    /* skip length, should be 16 */
385         *response++ = MS_CHAP2_RESPONSE_LEN;
386         cache_entry = chapms2_find_in_response_cache(id, challenge, NULL);
387         if (cache_entry) {
388                 memcpy(response, cache_entry->response, MS_CHAP2_RESPONSE_LEN);
389                 return;
390         }
391         ChapMS2(challenge,
392 #ifdef DEBUGMPPEKEY
393                 mschap2_peer_challenge,
394 #else
395                 NULL,
396 #endif
397                 our_name, secret, secret_len, response, auth_response,
398                 MS_CHAP2_AUTHENTICATEE);
399         chapms2_add_to_response_cache(id, challenge, response, auth_response);
400 }
401
402 static int
403 chapms2_check_success(int id, unsigned char *msg, int len)
404 {
405         if ((len < MS_AUTH_RESPONSE_LENGTH + 2) ||
406             strncmp((char *)msg, "S=", 2) != 0) {
407                 /* Packet does not start with "S=" */
408                 error("MS-CHAPv2 Success packet is badly formed.");
409                 return 0;
410         }
411         msg += 2;
412         len -= 2;
413         if (len < MS_AUTH_RESPONSE_LENGTH
414             || !chapms2_find_in_response_cache(id, NULL /* challenge */, msg)) {
415                 /* Authenticator Response did not match expected. */
416                 error("MS-CHAPv2 mutual authentication failed.");
417                 return 0;
418         }
419         /* Authenticator Response matches. */
420         msg += MS_AUTH_RESPONSE_LENGTH; /* Eat it */
421         len -= MS_AUTH_RESPONSE_LENGTH;
422         if ((len >= 3) && !strncmp((char *)msg, " M=", 3)) {
423                 msg += 3; /* Eat the delimiter */
424         } else  if ((len >= 2) && !strncmp((char *)msg, "M=", 2)) {
425                 msg += 2; /* Eat the delimiter */
426         } else if (len) {
427                 /* Packet has extra text which does not begin " M=" */
428                 error("MS-CHAPv2 Success packet is badly formed.");
429                 return 0;
430         }
431         return 1;
432 }
433
434 static void
435 chapms_handle_failure(unsigned char *inp, int len)
436 {
437         int err;
438         char *p, *msg;
439
440         /* We want a null-terminated string for strxxx(). */
441         msg = malloc(len + 1);
442         if (!msg) {
443                 notice("Out of memory in chapms_handle_failure");
444                 return;
445         }
446         BCOPY(inp, msg, len);
447         msg[len] = 0;
448         p = msg;
449
450         /*
451          * Deal with MS-CHAP formatted failure messages; just print the
452          * M=<message> part (if any).  For MS-CHAP we're not really supposed
453          * to use M=<message>, but it shouldn't hurt.  See
454          * chapms[2]_verify_response.
455          */
456         if (!strncmp(p, "E=", 2))
457                 err = strtol(p+2, NULL, 10); /* Remember the error code. */
458         else
459                 goto print_msg; /* Message is badly formatted. */
460
461         if (len && ((p = strstr(p, " M=")) != NULL)) {
462                 /* M=<message> field found. */
463                 p += 3;
464         } else {
465                 /* No M=<message>; use the error code. */
466                 switch (err) {
467                 case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
468                         p = "E=646 Restricted logon hours";
469                         break;
470
471                 case MS_CHAP_ERROR_ACCT_DISABLED:
472                         p = "E=647 Account disabled";
473                         break;
474
475                 case MS_CHAP_ERROR_PASSWD_EXPIRED:
476                         p = "E=648 Password expired";
477                         break;
478
479                 case MS_CHAP_ERROR_NO_DIALIN_PERMISSION:
480                         p = "E=649 No dialin permission";
481                         break;
482
483                 case MS_CHAP_ERROR_AUTHENTICATION_FAILURE:
484                         p = "E=691 Authentication failure";
485                         break;
486
487                 case MS_CHAP_ERROR_CHANGING_PASSWORD:
488                         /* Should never see this, we don't support Change Password. */
489                         p = "E=709 Error changing password";
490                         break;
491
492                 default:
493                         free(msg);
494                         error("Unknown MS-CHAP authentication failure: %.*v",
495                               len, inp);
496                         return;
497                 }
498         }
499 print_msg:
500         if (p != NULL)
501                 error("MS-CHAP authentication failed: %v", p);
502         free(msg);
503 }
504
505 static int
506 ChallengeResponse(u_char *challenge,
507                   u_char *PasswordHash,
508                   u_char *response)
509 {
510     u_char ZPasswordHash[21];
511     PPP_CIPHER_CTX *ctx;
512     int outlen = 0;
513     int offset = 0;
514     int retval = 0;
515
516     BZERO(ZPasswordHash, sizeof(ZPasswordHash));
517     BCOPY(PasswordHash, ZPasswordHash, MD4_DIGEST_LENGTH);
518
519 #if 0
520     dbglog("ChallengeResponse - ZPasswordHash %.*B",
521            sizeof(ZPasswordHash), ZPasswordHash);
522 #endif
523
524     ctx = PPP_CIPHER_CTX_new();
525     if (ctx != NULL) {
526
527         if (PPP_CipherInit(ctx, PPP_des_ecb(), ZPasswordHash + 0, NULL, 1)) {
528
529             if (PPP_CipherUpdate(ctx, response + offset, &outlen, challenge, 8)) {
530                 offset += outlen;
531
532                 PPP_CIPHER_CTX_set_cipher_data(ctx, ZPasswordHash + 7);
533                 if (PPP_CipherUpdate(ctx, response + offset, &outlen, challenge, 8)) {
534                     offset += outlen;
535
536                     PPP_CIPHER_CTX_set_cipher_data(ctx, ZPasswordHash + 14);
537                     if (PPP_CipherUpdate(ctx, response + offset, &outlen, challenge, 8)) {
538                         offset += outlen;
539
540                         if (PPP_CipherFinal(ctx, response + offset, &outlen)) {
541
542                             retval = 1;
543                         }
544                     }
545                 }
546             }
547         }
548
549         PPP_CIPHER_CTX_free(ctx);
550     }
551
552 #if 0
553     dbglog("ChallengeResponse - response %.24B", response);
554 #endif
555     return retval;
556 }
557
558 void
559 ChallengeHash(u_char PeerChallenge[16], u_char *rchallenge,
560               char *username, u_char Challenge[8])
561     
562 {
563     PPP_MD_CTX* ctx;
564     u_char      hash[SHA_DIGEST_LENGTH];
565     int     hash_len;
566     char        *user;
567
568     /* remove domain from "domain\username" */
569     if ((user = strrchr(username, '\\')) != NULL)
570         ++user;
571     else
572         user = username;
573     
574     ctx = PPP_MD_CTX_new();
575     if (ctx != NULL) {
576
577         if (PPP_DigestInit(ctx, PPP_sha1())) {
578
579             if (PPP_DigestUpdate(ctx, PeerChallenge, 16)) {
580
581                 if (PPP_DigestUpdate(ctx, rchallenge, 16)) {
582
583                     if (PPP_DigestUpdate(ctx, user, strlen(user))) {
584                         
585                         hash_len = SHA_DIGEST_LENGTH;
586                         if (PPP_DigestFinal(ctx, hash, &hash_len)) {
587
588                             BCOPY(hash, Challenge, 8);
589                         }
590                     }
591                 }
592             }
593         }
594
595         PPP_MD_CTX_free(ctx);
596     }
597 }
598
599 /*
600  * Convert the ASCII version of the password to Unicode.
601  * This implicitly supports 8-bit ISO8859/1 characters.
602  * This gives us the little-endian representation, which
603  * is assumed by all M$ CHAP RFCs.  (Unicode byte ordering
604  * is machine-dependent.)
605  */
606 static void
607 ascii2unicode(char ascii[], int ascii_len, u_char unicode[])
608 {
609     int i;
610
611     BZERO(unicode, ascii_len * 2);
612     for (i = 0; i < ascii_len; i++)
613         unicode[i * 2] = (u_char) ascii[i];
614 }
615
616 static void
617 NTPasswordHash(u_char *secret, int secret_len, unsigned char* hash)
618 {
619     PPP_MD_CTX* ctx = PPP_MD_CTX_new();
620     if (ctx != NULL) {
621
622         if (PPP_DigestInit(ctx, PPP_md4())) {
623
624             if (PPP_DigestUpdate(ctx, secret, secret_len)) {
625
626                 int hash_len = MD4_DIGEST_LENGTH;
627                 PPP_DigestFinal(ctx, hash, &hash_len);
628             }
629         }
630         
631         PPP_MD_CTX_free(ctx);
632     }
633 }
634
635 static void
636 ChapMS_NT(u_char *rchallenge, char *secret, int secret_len,
637           u_char NTResponse[24])
638 {
639     u_char      unicodePassword[MAX_NT_PASSWORD * 2];
640     u_char      PasswordHash[MD4_DIGEST_LENGTH];
641
642     /* Hash the Unicode version of the secret (== password). */
643     ascii2unicode(secret, secret_len, unicodePassword);
644     NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
645
646     ChallengeResponse(rchallenge, PasswordHash, NTResponse);
647 }
648
649 static void
650 ChapMS2_NT(u_char *rchallenge, u_char PeerChallenge[16], char *username,
651            char *secret, int secret_len, u_char NTResponse[24])
652 {
653     u_char      unicodePassword[MAX_NT_PASSWORD * 2];
654     u_char      PasswordHash[MD4_DIGEST_LENGTH];
655     u_char      Challenge[8];
656
657     ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
658
659     /* Hash the Unicode version of the secret (== password). */
660     ascii2unicode(secret, secret_len, unicodePassword);
661     NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
662
663     ChallengeResponse(Challenge, PasswordHash, NTResponse);
664 }
665
666 #ifdef PPP_WITH_MSLANMAN
667 static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
668
669 static void
670 ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len,
671               unsigned char *response)
672 {
673     int                 i;
674     u_char              UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
675     u_char              PasswordHash[MD4_DIGEST_LENGTH];
676
677     /* LANMan password is case insensitive */
678     BZERO(UcasePassword, sizeof(UcasePassword));
679     for (i = 0; i < secret_len; i++)
680        UcasePassword[i] = (u_char)toupper(secret[i]);
681     (void) DesSetkey(UcasePassword + 0);
682     DesEncrypt( StdText, PasswordHash + 0 );
683     (void) DesSetkey(UcasePassword + 7);
684     DesEncrypt( StdText, PasswordHash + 8 );
685     ChallengeResponse(rchallenge, PasswordHash, &response[MS_CHAP_LANMANRESP]);
686 }
687 #endif
688
689
690 void
691 GenerateAuthenticatorResponse(unsigned char* PasswordHashHash,
692                               unsigned char *NTResponse, unsigned char *PeerChallenge,
693                               unsigned char *rchallenge, char *username,
694                               unsigned char *authResponse)
695 {
696     /*
697      * "Magic" constants used in response generation, from RFC 2759.
698      */
699     u_char Magic1[39] = /* "Magic server to client signing constant" */
700         { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
701           0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
702           0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
703           0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
704     u_char Magic2[41] = /* "Pad to make it do more than one iteration" */
705         { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
706           0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
707           0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
708           0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
709           0x6E };
710
711     int         i;
712     PPP_MD_CTX *ctx;
713     u_char      Digest[SHA_DIGEST_LENGTH];
714     int     hash_len;
715     u_char      Challenge[8];
716
717     ctx = PPP_MD_CTX_new();
718     if (ctx != NULL) {
719
720         if (PPP_DigestInit(ctx, PPP_sha1())) {
721
722             if (PPP_DigestUpdate(ctx, PasswordHashHash, MD4_DIGEST_LENGTH)) {
723
724                 if (PPP_DigestUpdate(ctx, NTResponse, 24)) {
725
726                     if (PPP_DigestUpdate(ctx, Magic1, sizeof(Magic1))) {
727                         
728                         hash_len = sizeof(Digest);
729                         PPP_DigestFinal(ctx, Digest, &hash_len);
730                     }
731                 }
732             }
733         }
734         PPP_MD_CTX_free(ctx);
735     }
736     
737     ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
738
739     ctx = PPP_MD_CTX_new();
740     if (ctx != NULL) {
741
742         if (PPP_DigestInit(ctx, PPP_sha1())) {
743
744             if (PPP_DigestUpdate(ctx, Digest, sizeof(Digest))) {
745
746                 if (PPP_DigestUpdate(ctx, Challenge, sizeof(Challenge))) {
747
748                     if (PPP_DigestUpdate(ctx, Magic2, sizeof(Magic2))) {
749                         
750                         hash_len = sizeof(Digest);
751                         PPP_DigestFinal(ctx, Digest, &hash_len);
752                     }
753                 }
754             }
755         }
756
757         PPP_MD_CTX_free(ctx);
758     }
759
760     /* Convert to ASCII hex string. */
761     for (i = 0; i < MAX((MS_AUTH_RESPONSE_LENGTH / 2), sizeof(Digest)); i++) {
762         sprintf((char *)&authResponse[i * 2], "%02X", Digest[i]);
763     }
764 }
765
766
767 static void
768 GenerateAuthenticatorResponsePlain
769                 (char *secret, int secret_len,
770                  u_char NTResponse[24], u_char PeerChallenge[16],
771                  u_char *rchallenge, char *username,
772                  u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
773 {
774     u_char      unicodePassword[MAX_NT_PASSWORD * 2];
775     u_char      PasswordHash[MD4_DIGEST_LENGTH];
776     u_char      PasswordHashHash[MD4_DIGEST_LENGTH];
777
778     /* Hash (x2) the Unicode version of the secret (== password). */
779     ascii2unicode(secret, secret_len, unicodePassword);
780     NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
781     NTPasswordHash(PasswordHash, sizeof(PasswordHash),
782                    PasswordHashHash);
783
784     GenerateAuthenticatorResponse(PasswordHashHash, NTResponse, PeerChallenge,
785                                   rchallenge, username, authResponse);
786 }
787
788
789 #ifdef PPP_WITH_MPPE
790
791 /*
792  * Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079)
793  */
794 static void
795 Set_Start_Key(u_char *rchallenge, char *secret, int secret_len)
796 {
797     u_char      unicodePassword[MAX_NT_PASSWORD * 2];
798     u_char      PasswordHash[MD4_DIGEST_LENGTH];
799     u_char      PasswordHashHash[MD4_DIGEST_LENGTH];
800
801     /* Hash (x2) the Unicode version of the secret (== password). */
802     ascii2unicode(secret, secret_len, unicodePassword);
803     NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
804     NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
805
806     mppe_set_chapv1(rchallenge, PasswordHashHash);
807 }
808
809 /*
810  * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
811  */
812 static void
813 SetMasterKeys(char *secret, int secret_len, u_char NTResponse[24], int IsServer)
814 {
815     u_char      unicodePassword[MAX_NT_PASSWORD * 2];
816     u_char      PasswordHash[MD4_DIGEST_LENGTH];
817     u_char      PasswordHashHash[MD4_DIGEST_LENGTH];
818     /* Hash (x2) the Unicode version of the secret (== password). */
819     ascii2unicode(secret, secret_len, unicodePassword);
820     NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
821     NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
822     mppe_set_chapv2(PasswordHashHash, NTResponse, IsServer);
823 }
824
825 #endif /* PPP_WITH_MPPE */
826
827
828 void
829 ChapMS(u_char *rchallenge, char *secret, int secret_len,
830        unsigned char *response)
831 {
832     BZERO(response, MS_CHAP_RESPONSE_LEN);
833
834     ChapMS_NT(rchallenge, secret, secret_len, &response[MS_CHAP_NTRESP]);
835
836 #ifdef PPP_WITH_MSLANMAN
837     ChapMS_LANMan(rchallenge, secret, secret_len,
838                   &response[MS_CHAP_LANMANRESP]);
839
840     /* preferred method is set by option  */
841     response[MS_CHAP_USENT] = !ms_lanman;
842 #else
843     response[MS_CHAP_USENT] = 1;
844 #endif
845
846 #ifdef PPP_WITH_MPPE
847     Set_Start_Key(rchallenge, secret, secret_len);
848 #endif
849 }
850
851
852 /*
853  * If PeerChallenge is NULL, one is generated and the PeerChallenge
854  * field of response is filled in.  Call this way when generating a response.
855  * If PeerChallenge is supplied, it is copied into the PeerChallenge field.
856  * Call this way when verifying a response (or debugging).
857  * Do not call with PeerChallenge = response.
858  *
859  * The PeerChallenge field of response is then used for calculation of the
860  * Authenticator Response.
861  */
862 void
863 ChapMS2(u_char *rchallenge, u_char *PeerChallenge,
864         char *user, char *secret, int secret_len, unsigned char *response,
865         u_char authResponse[], int authenticator)
866 {
867     /* ARGSUSED */
868     u_char *p = &response[MS_CHAP2_PEER_CHALLENGE];
869     int i;
870
871     BZERO(response, MS_CHAP2_RESPONSE_LEN);
872
873     /* Generate the Peer-Challenge if requested, or copy it if supplied. */
874     if (!PeerChallenge)
875         for (i = 0; i < MS_CHAP2_PEER_CHAL_LEN; i++)
876             *p++ = (u_char) (drand48() * 0xff);
877     else
878         BCOPY(PeerChallenge, &response[MS_CHAP2_PEER_CHALLENGE],
879               MS_CHAP2_PEER_CHAL_LEN);
880
881     /* Generate the NT-Response */
882     ChapMS2_NT(rchallenge, &response[MS_CHAP2_PEER_CHALLENGE], user,
883                secret, secret_len, &response[MS_CHAP2_NTRESP]);
884
885     /* Generate the Authenticator Response. */
886     GenerateAuthenticatorResponsePlain(secret, secret_len,
887                                        &response[MS_CHAP2_NTRESP],
888                                        &response[MS_CHAP2_PEER_CHALLENGE],
889                                        rchallenge, user, authResponse);
890
891 #ifdef PPP_WITH_MPPE
892     SetMasterKeys(secret, secret_len,
893                   &response[MS_CHAP2_NTRESP], authenticator);
894 #endif
895 }
896
897
898 static struct chap_digest_type chapms_digest = {
899         CHAP_MICROSOFT,         /* code */
900         chapms_generate_challenge,
901         chapms_verify_response,
902         chapms_make_response,
903         NULL,                   /* check_success */
904         chapms_handle_failure,
905 };
906
907 static struct chap_digest_type chapms2_digest = {
908         CHAP_MICROSOFT_V2,      /* code */
909         chapms2_generate_challenge,
910         chapms2_verify_response,
911         chapms2_make_response,
912         chapms2_check_success,
913         chapms_handle_failure,
914 };
915
916 #ifndef UNIT_TEST
917 void
918 chapms_init(void)
919 {
920         chap_register_digest(&chapms_digest);
921         chap_register_digest(&chapms2_digest);
922         add_options(chapms_option_list);
923 }
924 #else
925
926 #include <time.h>
927
928 int debug = 1;
929 int error_count = 0;
930 int unsuccess = 0;
931
932 void random_bytes(unsigned char *bytes, int len)
933 {
934     int i = 0;
935     srand(time(NULL));
936     while (i < len) {
937         bytes[i++] = (unsigned char) rand();
938     }
939 }
940
941
942 int test_chap_v1(void) {
943     char *secret = "MyPw";
944
945     unsigned char challenge[8] = {
946         0x10, 0x2D, 0xB5, 0xDF, 0x08, 0x5D, 0x30, 0x41
947     };
948     unsigned char response[MS_CHAP_RESPONSE_LEN] = {
949     };
950     unsigned char result[MS_CHAP_RESPONSE_LEN] = {
951         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
952         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
953         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
954
955         0x4E, 0x9D, 0x3C, 0x8F, 0x9C, 0xFD, 0x38, 0x5D,
956         0x5B, 0xF4, 0xD3, 0x24, 0x67, 0x91, 0x95, 0x6C,
957         0xA4, 0xC3, 0x51, 0xAB, 0x40, 0x9A, 0x3D, 0x61,
958
959         0x01
960     };
961
962     ChapMS(challenge, secret, strlen(secret), response);
963     return memcmp(response, result, MS_CHAP_RESPONSE_LEN);
964 }
965
966 int test_chap_v2(void) {
967     char *secret = "clientPass";
968     char *name = "User";
969
970     char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
971     char *saresult = "407A5589115FD0D6209F510FE9C04566932CDA56";
972
973     unsigned char authenticator[16] = {
974         0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E,
975         0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28
976     };
977     unsigned char peerchallenge[16] = {
978         0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A,
979         0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E
980     };
981     unsigned char result[MS_CHAP_NTRESP_LEN] = {
982         0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E,
983         0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54,
984         0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF
985     };
986
987     unsigned char response[MS_CHAP2_RESPONSE_LEN] = {
988     };
989
990         ChapMS2(authenticator, peerchallenge, name,
991                 secret, strlen(secret), response,
992                 (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR);
993
994     return memcmp(&response[MS_CHAP2_NTRESP], result, MS_CHAP2_NTRESP_LEN) ||
995         strncmp(saresponse, saresult, MS_AUTH_RESPONSE_LENGTH);
996 }
997
998 int main(int argc, char *argv[]) {
999     
1000     PPP_crypto_init();
1001
1002     if (test_chap_v1()) {
1003         printf("CHAPv1 failed\n");
1004         return -1;
1005     }
1006
1007     if (test_chap_v2()) {
1008         printf("CHAPv2 failed\n");
1009         return -1;
1010     }
1011
1012     PPP_crypto_deinit();
1013
1014     printf("Success\n");
1015     return 0;
1016 }
1017
1018 #endif  /* UNIT_TEST */
1019