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