]> git.ozlabs.org Git - ppp.git/blob - pppd/chap_ms.c
Use autoconf/automake to configure and make ppp
[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 #ifdef CHAPMS
84
85 #include <stdio.h>
86 #include <stdlib.h>
87 #include <string.h>
88 #include <ctype.h>
89 #include <sys/types.h>
90 #include <sys/time.h>
91 #include <unistd.h>
92
93 #include "pppd.h"
94 #include "chap-new.h"
95 #include "chap_ms.h"
96 #include "md4.h"
97 #include "sha1.h"
98 #include "pppcrypt.h"
99 #include "magic.h"
100 #include "mppe.h"
101
102 static void     ascii2unicode (char[], int, u_char[]);
103 static void     NTPasswordHash (u_char *, int, u_char[MD4_SIGNATURE_SIZE]);
104 static void     ChallengeResponse (u_char *, u_char *, u_char[24]);
105 static void     ChapMS_NT (u_char *, char *, int, u_char[24]);
106 static void     ChapMS2_NT (u_char *, u_char[16], char *, char *, int,
107                                 u_char[24]);
108 static void     GenerateAuthenticatorResponsePlain
109                         (char*, int, u_char[24], u_char[16], u_char *,
110                          char *, u_char[41]);
111 #ifdef MSLANMAN
112 static void     ChapMS_LANMan (u_char *, char *, int, u_char *);
113 #endif
114
115 #ifdef MSLANMAN
116 bool    ms_lanman = 0;          /* Use LanMan password instead of NT */
117                                 /* Has meaning only with MS-CHAP challenges */
118 #endif
119
120 #ifdef MPPE
121 #ifdef DEBUGMPPEKEY
122 /* For MPPE debug */
123 /* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */
124 static char *mschap_challenge = NULL;
125 /* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */
126 static char *mschap2_peer_challenge = NULL;
127 #endif
128
129 #include "fsm.h"                /* Need to poke MPPE options */
130 #include "ccp.h"
131 #include <net/ppp-comp.h>
132 #endif
133
134 /*
135  * Command-line options.
136  */
137 static option_t chapms_option_list[] = {
138 #ifdef MSLANMAN
139         { "ms-lanman", o_bool, &ms_lanman,
140           "Use LanMan passwd when using MS-CHAP", 1 },
141 #endif
142 #ifdef DEBUGMPPEKEY
143         { "mschap-challenge", o_string, &mschap_challenge,
144           "specify CHAP challenge" },
145         { "mschap2-peer-challenge", o_string, &mschap2_peer_challenge,
146           "specify CHAP peer challenge" },
147 #endif
148         { NULL }
149 };
150
151 /*
152  * chapms_generate_challenge - generate a challenge for MS-CHAP.
153  * For MS-CHAP the challenge length is fixed at 8 bytes.
154  * The length goes in challenge[0] and the actual challenge starts
155  * at challenge[1].
156  */
157 static void
158 chapms_generate_challenge(unsigned char *challenge)
159 {
160         *challenge++ = 8;
161 #ifdef DEBUGMPPEKEY
162         if (mschap_challenge && strlen(mschap_challenge) == 8)
163                 memcpy(challenge, mschap_challenge, 8);
164         else
165 #endif
166                 random_bytes(challenge, 8);
167 }
168
169 static void
170 chapms2_generate_challenge(unsigned char *challenge)
171 {
172         *challenge++ = 16;
173 #ifdef DEBUGMPPEKEY
174         if (mschap_challenge && strlen(mschap_challenge) == 16)
175                 memcpy(challenge, mschap_challenge, 16);
176         else
177 #endif
178                 random_bytes(challenge, 16);
179 }
180
181 static int
182 chapms_verify_response(int id, char *name,
183                        unsigned char *secret, int secret_len,
184                        unsigned char *challenge, unsigned char *response,
185                        char *message, int message_space)
186 {
187         unsigned char md[MS_CHAP_RESPONSE_LEN];
188         int diff;
189         int challenge_len, response_len;
190
191         challenge_len = *challenge++;   /* skip length, is 8 */
192         response_len = *response++;
193         if (response_len != MS_CHAP_RESPONSE_LEN)
194                 goto bad;
195
196 #ifndef MSLANMAN
197         if (!response[MS_CHAP_USENT]) {
198                 /* Should really propagate this into the error packet. */
199                 notice("Peer request for LANMAN auth not supported");
200                 goto bad;
201         }
202 #endif
203
204         /* Generate the expected response. */
205         ChapMS(challenge, (char *)secret, secret_len, md);
206
207 #ifdef MSLANMAN
208         /* Determine which part of response to verify against */
209         if (!response[MS_CHAP_USENT])
210                 diff = memcmp(&response[MS_CHAP_LANMANRESP],
211                               &md[MS_CHAP_LANMANRESP], MS_CHAP_LANMANRESP_LEN);
212         else
213 #endif
214                 diff = memcmp(&response[MS_CHAP_NTRESP], &md[MS_CHAP_NTRESP],
215                               MS_CHAP_NTRESP_LEN);
216
217         if (diff == 0) {
218                 slprintf(message, message_space, "Access granted");
219                 return 1;
220         }
221
222  bad:
223         /* See comments below for MS-CHAP V2 */
224         slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
225                  challenge_len, challenge);
226         return 0;
227 }
228
229 static int
230 chapms2_verify_response(int id, char *name,
231                         unsigned char *secret, int secret_len,
232                         unsigned char *challenge, unsigned char *response,
233                         char *message, int message_space)
234 {
235         unsigned char md[MS_CHAP2_RESPONSE_LEN];
236         char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
237         int challenge_len, response_len;
238
239         challenge_len = *challenge++;   /* skip length, is 16 */
240         response_len = *response++;
241         if (response_len != MS_CHAP2_RESPONSE_LEN)
242                 goto bad;       /* not even the right length */
243
244         /* Generate the expected response and our mutual auth. */
245         ChapMS2(challenge, &response[MS_CHAP2_PEER_CHALLENGE], name,
246                 (char *)secret, secret_len, md,
247                 (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR);
248
249         /* compare MDs and send the appropriate status */
250         /*
251          * Per RFC 2759, success message must be formatted as
252          *     "S=<auth_string> M=<message>"
253          * where
254          *     <auth_string> is the Authenticator Response (mutual auth)
255          *     <message> is a text message
256          *
257          * However, some versions of Windows (win98 tested) do not know
258          * about the M=<message> part (required per RFC 2759) and flag
259          * it as an error (reported incorrectly as an encryption error
260          * to the user).  Since the RFC requires it, and it can be
261          * useful information, we supply it if the peer is a conforming
262          * system.  Luckily (?), win98 sets the Flags field to 0x04
263          * (contrary to RFC requirements) so we can use that to
264          * distinguish between conforming and non-conforming systems.
265          *
266          * Special thanks to Alex Swiridov <say@real.kharkov.ua> for
267          * help debugging this.
268          */
269         if (memcmp(&md[MS_CHAP2_NTRESP], &response[MS_CHAP2_NTRESP],
270                    MS_CHAP2_NTRESP_LEN) == 0) {
271                 if (response[MS_CHAP2_FLAGS])
272                         slprintf(message, message_space, "S=%s", saresponse);
273                 else
274                         slprintf(message, message_space, "S=%s M=%s",
275                                  saresponse, "Access granted");
276                 return 1;
277         }
278
279  bad:
280         /*
281          * Failure message must be formatted as
282          *     "E=e R=r C=c V=v M=m"
283          * where
284          *     e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE)
285          *     r = retry (we use 1, ok to retry)
286          *     c = challenge to use for next response, we reuse previous
287          *     v = Change Password version supported, we use 0
288          *     m = text message
289          *
290          * The M=m part is only for MS-CHAPv2.  Neither win2k nor
291          * win98 (others untested) display the message to the user anyway.
292          * They also both ignore the E=e code.
293          *
294          * Note that it's safe to reuse the same challenge as we don't
295          * actually accept another response based on the error message
296          * (and no clients try to resend a response anyway).
297          *
298          * Basically, this whole bit is useless code, even the small
299          * implementation here is only because of overspecification.
300          */
301         slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
302                  challenge_len, challenge, "Access denied");
303         return 0;
304 }
305
306 static void
307 chapms_make_response(unsigned char *response, int id, char *our_name,
308                      unsigned char *challenge, char *secret, int secret_len,
309                      unsigned char *private)
310 {
311         challenge++;    /* skip length, should be 8 */
312         *response++ = MS_CHAP_RESPONSE_LEN;
313         ChapMS(challenge, secret, secret_len, response);
314 }
315
316 struct chapms2_response_cache_entry {
317         int id;
318         unsigned char challenge[16];
319         unsigned char response[MS_CHAP2_RESPONSE_LEN];
320         unsigned char auth_response[MS_AUTH_RESPONSE_LENGTH];
321 };
322
323 #define CHAPMS2_MAX_RESPONSE_CACHE_SIZE 10
324 static struct chapms2_response_cache_entry
325     chapms2_response_cache[CHAPMS2_MAX_RESPONSE_CACHE_SIZE];
326 static int chapms2_response_cache_next_index = 0;
327 static int chapms2_response_cache_size = 0;
328
329 static void
330 chapms2_add_to_response_cache(int id, unsigned char *challenge,
331                               unsigned char *response,
332                               unsigned char *auth_response)
333 {
334         int i = chapms2_response_cache_next_index;
335
336         chapms2_response_cache[i].id = id;
337         memcpy(chapms2_response_cache[i].challenge, challenge, 16);
338         memcpy(chapms2_response_cache[i].response, response,
339                MS_CHAP2_RESPONSE_LEN);
340         memcpy(chapms2_response_cache[i].auth_response,
341                auth_response, MS_AUTH_RESPONSE_LENGTH);
342         chapms2_response_cache_next_index =
343                 (i + 1) % CHAPMS2_MAX_RESPONSE_CACHE_SIZE;
344         if (chapms2_response_cache_next_index > chapms2_response_cache_size)
345                 chapms2_response_cache_size = chapms2_response_cache_next_index;
346         dbglog("added response cache entry %d", i);
347 }
348
349 static struct chapms2_response_cache_entry*
350 chapms2_find_in_response_cache(int id, unsigned char *challenge,
351                       unsigned char *auth_response)
352 {
353         int i;
354
355         for (i = 0; i < chapms2_response_cache_size; i++) {
356                 if (id == chapms2_response_cache[i].id
357                     && (!challenge
358                         || memcmp(challenge,
359                                   chapms2_response_cache[i].challenge,
360                                   16) == 0)
361                     && (!auth_response
362                         || memcmp(auth_response,
363                                   chapms2_response_cache[i].auth_response,
364                                   MS_AUTH_RESPONSE_LENGTH) == 0)) {
365                         dbglog("response found in cache (entry %d)", i);
366                         return &chapms2_response_cache[i];
367                 }
368         }
369         return NULL;  /* not found */
370 }
371
372 static void
373 chapms2_make_response(unsigned char *response, int id, char *our_name,
374                       unsigned char *challenge, char *secret, int secret_len,
375                       unsigned char *private)
376 {
377         const struct chapms2_response_cache_entry *cache_entry;
378         unsigned char auth_response[MS_AUTH_RESPONSE_LENGTH+1];
379
380         challenge++;    /* skip length, should be 16 */
381         *response++ = MS_CHAP2_RESPONSE_LEN;
382         cache_entry = chapms2_find_in_response_cache(id, challenge, NULL);
383         if (cache_entry) {
384                 memcpy(response, cache_entry->response, MS_CHAP2_RESPONSE_LEN);
385                 return;
386         }
387         ChapMS2(challenge,
388 #ifdef DEBUGMPPEKEY
389                 mschap2_peer_challenge,
390 #else
391                 NULL,
392 #endif
393                 our_name, secret, secret_len, response, auth_response,
394                 MS_CHAP2_AUTHENTICATEE);
395         chapms2_add_to_response_cache(id, challenge, response, auth_response);
396 }
397
398 static int
399 chapms2_check_success(int id, unsigned char *msg, int len)
400 {
401         if ((len < MS_AUTH_RESPONSE_LENGTH + 2) ||
402             strncmp((char *)msg, "S=", 2) != 0) {
403                 /* Packet does not start with "S=" */
404                 error("MS-CHAPv2 Success packet is badly formed.");
405                 return 0;
406         }
407         msg += 2;
408         len -= 2;
409         if (len < MS_AUTH_RESPONSE_LENGTH
410             || !chapms2_find_in_response_cache(id, NULL /* challenge */, msg)) {
411                 /* Authenticator Response did not match expected. */
412                 error("MS-CHAPv2 mutual authentication failed.");
413                 return 0;
414         }
415         /* Authenticator Response matches. */
416         msg += MS_AUTH_RESPONSE_LENGTH; /* Eat it */
417         len -= MS_AUTH_RESPONSE_LENGTH;
418         if ((len >= 3) && !strncmp((char *)msg, " M=", 3)) {
419                 msg += 3; /* Eat the delimiter */
420         } else  if ((len >= 2) && !strncmp((char *)msg, "M=", 2)) {
421                 msg += 2; /* Eat the delimiter */
422         } else if (len) {
423                 /* Packet has extra text which does not begin " M=" */
424                 error("MS-CHAPv2 Success packet is badly formed.");
425                 return 0;
426         }
427         return 1;
428 }
429
430 static void
431 chapms_handle_failure(unsigned char *inp, int len)
432 {
433         int err;
434         char *p, *msg;
435
436         /* We want a null-terminated string for strxxx(). */
437         msg = malloc(len + 1);
438         if (!msg) {
439                 notice("Out of memory in chapms_handle_failure");
440                 return;
441         }
442         BCOPY(inp, msg, len);
443         msg[len] = 0;
444         p = msg;
445
446         /*
447          * Deal with MS-CHAP formatted failure messages; just print the
448          * M=<message> part (if any).  For MS-CHAP we're not really supposed
449          * to use M=<message>, but it shouldn't hurt.  See
450          * chapms[2]_verify_response.
451          */
452         if (!strncmp(p, "E=", 2))
453                 err = strtol(p+2, NULL, 10); /* Remember the error code. */
454         else
455                 goto print_msg; /* Message is badly formatted. */
456
457         if (len && ((p = strstr(p, " M=")) != NULL)) {
458                 /* M=<message> field found. */
459                 p += 3;
460         } else {
461                 /* No M=<message>; use the error code. */
462                 switch (err) {
463                 case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
464                         p = "E=646 Restricted logon hours";
465                         break;
466
467                 case MS_CHAP_ERROR_ACCT_DISABLED:
468                         p = "E=647 Account disabled";
469                         break;
470
471                 case MS_CHAP_ERROR_PASSWD_EXPIRED:
472                         p = "E=648 Password expired";
473                         break;
474
475                 case MS_CHAP_ERROR_NO_DIALIN_PERMISSION:
476                         p = "E=649 No dialin permission";
477                         break;
478
479                 case MS_CHAP_ERROR_AUTHENTICATION_FAILURE:
480                         p = "E=691 Authentication failure";
481                         break;
482
483                 case MS_CHAP_ERROR_CHANGING_PASSWORD:
484                         /* Should never see this, we don't support Change Password. */
485                         p = "E=709 Error changing password";
486                         break;
487
488                 default:
489                         free(msg);
490                         error("Unknown MS-CHAP authentication failure: %.*v",
491                               len, inp);
492                         return;
493                 }
494         }
495 print_msg:
496         if (p != NULL)
497                 error("MS-CHAP authentication failed: %v", p);
498         free(msg);
499 }
500
501 static void
502 ChallengeResponse(u_char *challenge,
503                   u_char PasswordHash[MD4_SIGNATURE_SIZE],
504                   u_char response[24])
505 {
506     u_char    ZPasswordHash[21];
507
508     BZERO(ZPasswordHash, sizeof(ZPasswordHash));
509     BCOPY(PasswordHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
510
511 #if 0
512     dbglog("ChallengeResponse - ZPasswordHash %.*B",
513            sizeof(ZPasswordHash), ZPasswordHash);
514 #endif
515
516     (void) DesSetkey(ZPasswordHash + 0);
517     DesEncrypt(challenge, response + 0);
518     (void) DesSetkey(ZPasswordHash + 7);
519     DesEncrypt(challenge, response + 8);
520     (void) DesSetkey(ZPasswordHash + 14);
521     DesEncrypt(challenge, response + 16);
522
523 #if 0
524     dbglog("ChallengeResponse - response %.24B", response);
525 #endif
526 }
527
528 void
529 ChallengeHash(u_char PeerChallenge[16], u_char *rchallenge,
530               char *username, u_char Challenge[8])
531     
532 {
533     SHA1_CTX    sha1Context;
534     u_char      sha1Hash[SHA1_SIGNATURE_SIZE];
535     char        *user;
536
537     /* remove domain from "domain\username" */
538     if ((user = strrchr(username, '\\')) != NULL)
539         ++user;
540     else
541         user = username;
542
543     SHA1_Init(&sha1Context);
544     SHA1_Update(&sha1Context, PeerChallenge, 16);
545     SHA1_Update(&sha1Context, rchallenge, 16);
546     SHA1_Update(&sha1Context, (unsigned char *)user, strlen(user));
547     SHA1_Final(sha1Hash, &sha1Context);
548
549     BCOPY(sha1Hash, Challenge, 8);
550 }
551
552 /*
553  * Convert the ASCII version of the password to Unicode.
554  * This implicitly supports 8-bit ISO8859/1 characters.
555  * This gives us the little-endian representation, which
556  * is assumed by all M$ CHAP RFCs.  (Unicode byte ordering
557  * is machine-dependent.)
558  */
559 static void
560 ascii2unicode(char ascii[], int ascii_len, u_char unicode[])
561 {
562     int i;
563
564     BZERO(unicode, ascii_len * 2);
565     for (i = 0; i < ascii_len; i++)
566         unicode[i * 2] = (u_char) ascii[i];
567 }
568
569 static void
570 NTPasswordHash(u_char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE])
571 {
572 #ifdef __NetBSD__
573     /* NetBSD uses the libc md4 routines which take bytes instead of bits */
574     int                 mdlen = secret_len;
575 #else
576     int                 mdlen = secret_len * 8;
577 #endif
578     MD4_CTX             md4Context;
579
580     MD4Init(&md4Context);
581     /* MD4Update can take at most 64 bytes at a time */
582     while (mdlen > 512) {
583         MD4Update(&md4Context, secret, 512);
584         secret += 64;
585         mdlen -= 512;
586     }
587     MD4Update(&md4Context, secret, mdlen);
588     MD4Final(hash, &md4Context);
589
590 }
591
592 static void
593 ChapMS_NT(u_char *rchallenge, char *secret, int secret_len,
594           u_char NTResponse[24])
595 {
596     u_char      unicodePassword[MAX_NT_PASSWORD * 2];
597     u_char      PasswordHash[MD4_SIGNATURE_SIZE];
598
599     /* Hash the Unicode version of the secret (== password). */
600     ascii2unicode(secret, secret_len, unicodePassword);
601     NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
602
603     ChallengeResponse(rchallenge, PasswordHash, NTResponse);
604 }
605
606 static void
607 ChapMS2_NT(u_char *rchallenge, u_char PeerChallenge[16], char *username,
608            char *secret, int secret_len, u_char NTResponse[24])
609 {
610     u_char      unicodePassword[MAX_NT_PASSWORD * 2];
611     u_char      PasswordHash[MD4_SIGNATURE_SIZE];
612     u_char      Challenge[8];
613
614     ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
615
616     /* Hash the Unicode version of the secret (== password). */
617     ascii2unicode(secret, secret_len, unicodePassword);
618     NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
619
620     ChallengeResponse(Challenge, PasswordHash, NTResponse);
621 }
622
623 #ifdef MSLANMAN
624 static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
625
626 static void
627 ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len,
628               unsigned char *response)
629 {
630     int                 i;
631     u_char              UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
632     u_char              PasswordHash[MD4_SIGNATURE_SIZE];
633
634     /* LANMan password is case insensitive */
635     BZERO(UcasePassword, sizeof(UcasePassword));
636     for (i = 0; i < secret_len; i++)
637        UcasePassword[i] = (u_char)toupper(secret[i]);
638     (void) DesSetkey(UcasePassword + 0);
639     DesEncrypt( StdText, PasswordHash + 0 );
640     (void) DesSetkey(UcasePassword + 7);
641     DesEncrypt( StdText, PasswordHash + 8 );
642     ChallengeResponse(rchallenge, PasswordHash, &response[MS_CHAP_LANMANRESP]);
643 }
644 #endif
645
646
647 void
648 GenerateAuthenticatorResponse(u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
649                               u_char NTResponse[24], u_char PeerChallenge[16],
650                               u_char *rchallenge, char *username,
651                               u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
652 {
653     /*
654      * "Magic" constants used in response generation, from RFC 2759.
655      */
656     u_char Magic1[39] = /* "Magic server to client signing constant" */
657         { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
658           0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
659           0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
660           0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
661     u_char Magic2[41] = /* "Pad to make it do more than one iteration" */
662         { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
663           0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
664           0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
665           0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
666           0x6E };
667
668     int         i;
669     SHA1_CTX    sha1Context;
670     u_char      Digest[SHA1_SIGNATURE_SIZE];
671     u_char      Challenge[8];
672
673     SHA1_Init(&sha1Context);
674     SHA1_Update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
675     SHA1_Update(&sha1Context, NTResponse, 24);
676     SHA1_Update(&sha1Context, Magic1, sizeof(Magic1));
677     SHA1_Final(Digest, &sha1Context);
678
679     ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
680
681     SHA1_Init(&sha1Context);
682     SHA1_Update(&sha1Context, Digest, sizeof(Digest));
683     SHA1_Update(&sha1Context, Challenge, sizeof(Challenge));
684     SHA1_Update(&sha1Context, Magic2, sizeof(Magic2));
685     SHA1_Final(Digest, &sha1Context);
686
687     /* Convert to ASCII hex string. */
688     for (i = 0; i < MAX((MS_AUTH_RESPONSE_LENGTH / 2), sizeof(Digest)); i++)
689         sprintf((char *)&authResponse[i * 2], "%02X", Digest[i]);
690 }
691
692
693 static void
694 GenerateAuthenticatorResponsePlain
695                 (char *secret, int secret_len,
696                  u_char NTResponse[24], u_char PeerChallenge[16],
697                  u_char *rchallenge, char *username,
698                  u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1])
699 {
700     u_char      unicodePassword[MAX_NT_PASSWORD * 2];
701     u_char      PasswordHash[MD4_SIGNATURE_SIZE];
702     u_char      PasswordHashHash[MD4_SIGNATURE_SIZE];
703
704     /* Hash (x2) the Unicode version of the secret (== password). */
705     ascii2unicode(secret, secret_len, unicodePassword);
706     NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
707     NTPasswordHash(PasswordHash, sizeof(PasswordHash),
708                    PasswordHashHash);
709
710     GenerateAuthenticatorResponse(PasswordHashHash, NTResponse, PeerChallenge,
711                                   rchallenge, username, authResponse);
712 }
713
714
715 #ifdef MPPE
716
717 /*
718  * Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079)
719  */
720 static void
721 Set_Start_Key(u_char *rchallenge, char *secret, int secret_len)
722 {
723     u_char      unicodePassword[MAX_NT_PASSWORD * 2];
724     u_char      PasswordHash[MD4_SIGNATURE_SIZE];
725     u_char      PasswordHashHash[MD4_SIGNATURE_SIZE];
726
727     /* Hash (x2) the Unicode version of the secret (== password). */
728     ascii2unicode(secret, secret_len, unicodePassword);
729     NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
730     NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
731
732     mppe_set_chapv1(rchallenge, PasswordHashHash);
733 }
734
735 /*
736  * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
737  */
738 static void
739 SetMasterKeys(char *secret, int secret_len, u_char NTResponse[24], int IsServer)
740 {
741     u_char      unicodePassword[MAX_NT_PASSWORD * 2];
742     u_char      PasswordHash[MD4_SIGNATURE_SIZE];
743     u_char      PasswordHashHash[MD4_SIGNATURE_SIZE];
744     /* Hash (x2) the Unicode version of the secret (== password). */
745     ascii2unicode(secret, secret_len, unicodePassword);
746     NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
747     NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
748     mppe_set_chapv2(PasswordHashHash, NTResponse, IsServer);
749 }
750
751 #endif /* MPPE */
752
753
754 void
755 ChapMS(u_char *rchallenge, char *secret, int secret_len,
756        unsigned char *response)
757 {
758     BZERO(response, MS_CHAP_RESPONSE_LEN);
759
760     ChapMS_NT(rchallenge, secret, secret_len, &response[MS_CHAP_NTRESP]);
761
762 #ifdef MSLANMAN
763     ChapMS_LANMan(rchallenge, secret, secret_len,
764                   &response[MS_CHAP_LANMANRESP]);
765
766     /* preferred method is set by option  */
767     response[MS_CHAP_USENT] = !ms_lanman;
768 #else
769     response[MS_CHAP_USENT] = 1;
770 #endif
771
772 #ifdef MPPE
773     Set_Start_Key(rchallenge, secret, secret_len);
774 #endif
775 }
776
777
778 /*
779  * If PeerChallenge is NULL, one is generated and the PeerChallenge
780  * field of response is filled in.  Call this way when generating a response.
781  * If PeerChallenge is supplied, it is copied into the PeerChallenge field.
782  * Call this way when verifying a response (or debugging).
783  * Do not call with PeerChallenge = response.
784  *
785  * The PeerChallenge field of response is then used for calculation of the
786  * Authenticator Response.
787  */
788 void
789 ChapMS2(u_char *rchallenge, u_char *PeerChallenge,
790         char *user, char *secret, int secret_len, unsigned char *response,
791         u_char authResponse[], int authenticator)
792 {
793     /* ARGSUSED */
794     u_char *p = &response[MS_CHAP2_PEER_CHALLENGE];
795     int i;
796
797     BZERO(response, MS_CHAP2_RESPONSE_LEN);
798
799     /* Generate the Peer-Challenge if requested, or copy it if supplied. */
800     if (!PeerChallenge)
801         for (i = 0; i < MS_CHAP2_PEER_CHAL_LEN; i++)
802             *p++ = (u_char) (drand48() * 0xff);
803     else
804         BCOPY(PeerChallenge, &response[MS_CHAP2_PEER_CHALLENGE],
805               MS_CHAP2_PEER_CHAL_LEN);
806
807     /* Generate the NT-Response */
808     ChapMS2_NT(rchallenge, &response[MS_CHAP2_PEER_CHALLENGE], user,
809                secret, secret_len, &response[MS_CHAP2_NTRESP]);
810
811     /* Generate the Authenticator Response. */
812     GenerateAuthenticatorResponsePlain(secret, secret_len,
813                                        &response[MS_CHAP2_NTRESP],
814                                        &response[MS_CHAP2_PEER_CHALLENGE],
815                                        rchallenge, user, authResponse);
816
817 #ifdef MPPE
818     SetMasterKeys(secret, secret_len,
819                   &response[MS_CHAP2_NTRESP], authenticator);
820 #endif
821 }
822
823
824 static struct chap_digest_type chapms_digest = {
825         CHAP_MICROSOFT,         /* code */
826         chapms_generate_challenge,
827         chapms_verify_response,
828         chapms_make_response,
829         NULL,                   /* check_success */
830         chapms_handle_failure,
831 };
832
833 static struct chap_digest_type chapms2_digest = {
834         CHAP_MICROSOFT_V2,      /* code */
835         chapms2_generate_challenge,
836         chapms2_verify_response,
837         chapms2_make_response,
838         chapms2_check_success,
839         chapms_handle_failure,
840 };
841
842 void
843 chapms_init(void)
844 {
845         chap_register_digest(&chapms_digest);
846         chap_register_digest(&chapms2_digest);
847         add_options(chapms_option_list);
848 }
849
850 #endif /* CHAPMS */