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