1/*	$NetBSD: chap-new.c,v 1.1.1.1 2005/02/20 10:28:41 cube Exp $	*/
2
3/*
4 * chap-new.c - New CHAP implementation.
5 *
6 * Copyright (c) 2003 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
16 *    endorse or promote products derived from this software without
17 *    prior written permission.
18 *
19 * 3. Redistributions of any form whatsoever must retain the following
20 *    acknowledgment:
21 *    "This product includes software developed by Paul Mackerras
22 *     <paulus@samba.org>".
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#include <sys/cdefs.h>
34#ifndef lint
35#if 0
36#define RCSID	"Id: chap-new.c,v 1.8 2005/07/13 10:41:58 paulus Exp"
37#else
38__RCSID("$NetBSD: chap-new.c,v 1.1.1.1 2005/02/20 10:28:41 cube Exp $");
39#endif
40#endif
41
42#include <stdlib.h>
43#include <string.h>
44#include "pppd.h"
45#include "chap-new.h"
46#include "chap-md5.h"
47
48#ifdef CHAPMS
49#include "chap_ms.h"
50#define MDTYPE_ALL (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT | MDTYPE_MD5)
51#else
52#define MDTYPE_ALL (MDTYPE_MD5)
53#endif
54
55int chap_mdtype_all = MDTYPE_ALL;
56
57/* Hook for a plugin to validate CHAP challenge */
58int (*chap_verify_hook)(char *name, char *ourname, int id,
59			struct chap_digest_type *digest,
60			unsigned char *challenge, unsigned char *response,
61			char *message, int message_space) = NULL;
62
63/*
64 * Option variables.
65 */
66int chap_timeout_time = 3;
67int chap_max_transmits = 10;
68int chap_rechallenge_time = 0;
69
70/*
71 * Command-line options.
72 */
73static option_t chap_option_list[] = {
74	{ "chap-restart", o_int, &chap_timeout_time,
75	  "Set timeout for CHAP", OPT_PRIO },
76	{ "chap-max-challenge", o_int, &chap_max_transmits,
77	  "Set max #xmits for challenge", OPT_PRIO },
78	{ "chap-interval", o_int, &chap_rechallenge_time,
79	  "Set interval for rechallenge", OPT_PRIO },
80	{ NULL }
81};
82
83/*
84 * Internal state.
85 */
86static struct chap_client_state {
87	int flags;
88	char *name;
89	struct chap_digest_type *digest;
90	unsigned char priv[64];		/* private area for digest's use */
91} client;
92
93/*
94 * These limits apply to challenge and response packets we send.
95 * The +4 is the +1 that we actually need rounded up.
96 */
97#define CHAL_MAX_PKTLEN	(PPP_HDRLEN + CHAP_HDRLEN + 4 + MAX_CHALLENGE_LEN + MAXNAMELEN)
98#define RESP_MAX_PKTLEN	(PPP_HDRLEN + CHAP_HDRLEN + 4 + MAX_RESPONSE_LEN + MAXNAMELEN)
99
100static struct chap_server_state {
101	int flags;
102	int id;
103	char *name;
104	struct chap_digest_type *digest;
105	int challenge_xmits;
106	int challenge_pktlen;
107	unsigned char challenge[CHAL_MAX_PKTLEN];
108	char message[256];
109} server;
110
111/* Values for flags in chap_client_state and chap_server_state */
112#define LOWERUP			1
113#define AUTH_STARTED		2
114#define AUTH_DONE		4
115#define AUTH_FAILED		8
116#define TIMEOUT_PENDING		0x10
117#define CHALLENGE_VALID		0x20
118
119/*
120 * Prototypes.
121 */
122static void chap_init(int unit);
123static void chap_lowerup(int unit);
124static void chap_lowerdown(int unit);
125static void chap_timeout(void *arg);
126static void chap_generate_challenge(struct chap_server_state *ss);
127static void chap_handle_response(struct chap_server_state *ss, int code,
128		unsigned char *pkt, int len);
129static int chap_verify_response(char *name, char *ourname, int id,
130		struct chap_digest_type *digest,
131		unsigned char *challenge, unsigned char *response,
132		char *message, int message_space);
133static void chap_respond(struct chap_client_state *cs, int id,
134		unsigned char *pkt, int len);
135static void chap_handle_status(struct chap_client_state *cs, int code, int id,
136		unsigned char *pkt, int len);
137static void chap_protrej(int unit);
138static void chap_input(int unit, unsigned char *pkt, int pktlen);
139static int chap_print_pkt(unsigned char *p, int plen,
140		void (*printer) __P((void *, char *, ...)), void *arg);
141
142/* List of digest types that we know about */
143static struct chap_digest_type *chap_digests;
144
145/*
146 * chap_init - reset to initial state.
147 */
148static void
149chap_init(int unit)
150{
151	memset(&client, 0, sizeof(client));
152	memset(&server, 0, sizeof(server));
153
154	chap_md5_init();
155#ifdef CHAPMS
156	chapms_init();
157#endif
158}
159
160/*
161 * Add a new digest type to the list.
162 */
163void
164chap_register_digest(struct chap_digest_type *dp)
165{
166	dp->next = chap_digests;
167	chap_digests = dp;
168}
169
170/*
171 * chap_lowerup - we can start doing stuff now.
172 */
173static void
174chap_lowerup(int unit)
175{
176	struct chap_client_state *cs = &client;
177	struct chap_server_state *ss = &server;
178
179	cs->flags |= LOWERUP;
180	ss->flags |= LOWERUP;
181	if (ss->flags & AUTH_STARTED)
182		chap_timeout(ss);
183}
184
185static void
186chap_lowerdown(int unit)
187{
188	struct chap_client_state *cs = &client;
189	struct chap_server_state *ss = &server;
190
191	cs->flags = 0;
192	if (ss->flags & TIMEOUT_PENDING)
193		UNTIMEOUT(chap_timeout, ss);
194	ss->flags = 0;
195}
196
197/*
198 * chap_auth_peer - Start authenticating the peer.
199 * If the lower layer is already up, we start sending challenges,
200 * otherwise we wait for the lower layer to come up.
201 */
202void
203chap_auth_peer(int unit, char *our_name, int digest_code)
204{
205	struct chap_server_state *ss = &server;
206	struct chap_digest_type *dp;
207
208	if (ss->flags & AUTH_STARTED) {
209		error("CHAP: peer authentication already started!");
210		return;
211	}
212	for (dp = chap_digests; dp != NULL; dp = dp->next)
213		if (dp->code == digest_code)
214			break;
215	if (dp == NULL)
216		fatal("CHAP digest 0x%x requested but not available",
217		      digest_code);
218
219	ss->digest = dp;
220	ss->name = our_name;
221	/* Start with a random ID value */
222	ss->id = (unsigned char)(drand48() * 256);
223	ss->flags |= AUTH_STARTED;
224	if (ss->flags & LOWERUP)
225		chap_timeout(ss);
226}
227
228/*
229 * chap_auth_with_peer - Prepare to authenticate ourselves to the peer.
230 * There isn't much to do until we receive a challenge.
231 */
232void
233chap_auth_with_peer(int unit, char *our_name, int digest_code)
234{
235	struct chap_client_state *cs = &client;
236	struct chap_digest_type *dp;
237
238	if (cs->flags & AUTH_STARTED) {
239		error("CHAP: authentication with peer already started!");
240		return;
241	}
242	for (dp = chap_digests; dp != NULL; dp = dp->next)
243		if (dp->code == digest_code)
244			break;
245	if (dp == NULL)
246		fatal("CHAP digest 0x%x requested but not available",
247		      digest_code);
248
249	cs->digest = dp;
250	cs->name = our_name;
251	cs->flags |= AUTH_STARTED;
252}
253
254/*
255 * chap_timeout - It's time to send another challenge to the peer.
256 * This could be either a retransmission of a previous challenge,
257 * or a new challenge to start re-authentication.
258 */
259static void
260chap_timeout(void *arg)
261{
262	struct chap_server_state *ss = arg;
263
264	ss->flags &= ~TIMEOUT_PENDING;
265	if ((ss->flags & CHALLENGE_VALID) == 0) {
266		ss->challenge_xmits = 0;
267		chap_generate_challenge(ss);
268		ss->flags |= CHALLENGE_VALID;
269	} else if (ss->challenge_xmits >= chap_max_transmits) {
270		ss->flags &= ~CHALLENGE_VALID;
271		ss->flags |= AUTH_DONE | AUTH_FAILED;
272		auth_peer_fail(0, PPP_CHAP);
273		return;
274	}
275
276	output(0, ss->challenge, ss->challenge_pktlen);
277	++ss->challenge_xmits;
278	ss->flags |= TIMEOUT_PENDING;
279	TIMEOUT(chap_timeout, arg, chap_timeout_time);
280}
281
282/*
283 * chap_generate_challenge - generate a challenge string and format
284 * the challenge packet in ss->challenge_pkt.
285 */
286static void
287chap_generate_challenge(struct chap_server_state *ss)
288{
289	int clen = 1, nlen, len;
290	unsigned char *p;
291
292	p = ss->challenge;
293	MAKEHEADER(p, PPP_CHAP);
294	p += CHAP_HDRLEN;
295	ss->digest->generate_challenge(p);
296	clen = *p;
297	nlen = strlen(ss->name);
298	memcpy(p + 1 + clen, ss->name, nlen);
299
300	len = CHAP_HDRLEN + 1 + clen + nlen;
301	ss->challenge_pktlen = PPP_HDRLEN + len;
302
303	p = ss->challenge + PPP_HDRLEN;
304	p[0] = CHAP_CHALLENGE;
305	p[1] = ++ss->id;
306	p[2] = len >> 8;
307	p[3] = len;
308}
309
310/*
311 * chap_handle_response - check the response to our challenge.
312 */
313static void
314chap_handle_response(struct chap_server_state *ss, int id,
315		     unsigned char *pkt, int len)
316{
317	int response_len, ok, mlen;
318	unsigned char *response, *p;
319	char *name = NULL;	/* initialized to shut gcc up */
320	int (*verifier)(char *, char *, int, struct chap_digest_type *,
321		unsigned char *, unsigned char *, char *, int);
322	char rname[MAXNAMELEN+1];
323
324	if ((ss->flags & LOWERUP) == 0)
325		return;
326	if (id != ss->challenge[PPP_HDRLEN+1] || len < 2)
327		return;
328	if (ss->flags & CHALLENGE_VALID) {
329		response = pkt;
330		GETCHAR(response_len, pkt);
331		len -= response_len + 1;	/* length of name */
332		name = (char *)pkt + response_len;
333		if (len < 0)
334			return;
335
336		if (ss->flags & TIMEOUT_PENDING) {
337			ss->flags &= ~TIMEOUT_PENDING;
338			UNTIMEOUT(chap_timeout, ss);
339		}
340
341		if (explicit_remote) {
342			name = remote_name;
343		} else {
344			/* Null terminate and clean remote name. */
345			slprintf(rname, sizeof(rname), "%.*v", len, name);
346			name = rname;
347		}
348
349		if (chap_verify_hook)
350			verifier = chap_verify_hook;
351		else
352			verifier = chap_verify_response;
353		ok = (*verifier)(name, ss->name, id, ss->digest,
354				 ss->challenge + PPP_HDRLEN + CHAP_HDRLEN,
355				 response, ss->message, sizeof(ss->message));
356		if (!ok || !auth_number()) {
357			ss->flags |= AUTH_FAILED;
358			warn("Peer %q failed CHAP authentication", name);
359		}
360	} else if ((ss->flags & AUTH_DONE) == 0)
361		return;
362
363	/* send the response */
364	p = outpacket_buf;
365	MAKEHEADER(p, PPP_CHAP);
366	mlen = strlen(ss->message);
367	len = CHAP_HDRLEN + mlen;
368	p[0] = (ss->flags & AUTH_FAILED)? CHAP_FAILURE: CHAP_SUCCESS;
369	p[1] = id;
370	p[2] = len >> 8;
371	p[3] = len;
372	if (mlen > 0)
373		memcpy(p + CHAP_HDRLEN, ss->message, mlen);
374	output(0, outpacket_buf, PPP_HDRLEN + len);
375
376	if (ss->flags & CHALLENGE_VALID) {
377		ss->flags &= ~CHALLENGE_VALID;
378		if (ss->flags & AUTH_FAILED) {
379			auth_peer_fail(0, PPP_CHAP);
380		} else {
381			if ((ss->flags & AUTH_DONE) == 0)
382				auth_peer_success(0, PPP_CHAP,
383						  ss->digest->code,
384						  name, strlen(name));
385			if (chap_rechallenge_time) {
386				ss->flags |= TIMEOUT_PENDING;
387				TIMEOUT(chap_timeout, ss,
388					chap_rechallenge_time);
389			}
390		}
391		ss->flags |= AUTH_DONE;
392	}
393}
394
395/*
396 * chap_verify_response - check whether the peer's response matches
397 * what we think it should be.  Returns 1 if it does (authentication
398 * succeeded), or 0 if it doesn't.
399 */
400static int
401chap_verify_response(char *name, char *ourname, int id,
402		     struct chap_digest_type *digest,
403		     unsigned char *challenge, unsigned char *response,
404		     char *message, int message_space)
405{
406	int ok;
407	unsigned char secret[MAXSECRETLEN];
408	int secret_len;
409
410	/* Get the secret that the peer is supposed to know */
411	if (!get_secret(0, name, ourname, (char *)secret, &secret_len, 1)) {
412		error("No CHAP secret found for authenticating %q", name);
413		return 0;
414	}
415
416	ok = digest->verify_response(id, name, secret, secret_len, challenge,
417				     response, message, message_space);
418	memset(secret, 0, sizeof(secret));
419
420	return ok;
421}
422
423/*
424 * chap_respond - Generate and send a response to a challenge.
425 */
426static void
427chap_respond(struct chap_client_state *cs, int id,
428	     unsigned char *pkt, int len)
429{
430	int clen, nlen;
431	int secret_len;
432	unsigned char *p;
433	unsigned char response[RESP_MAX_PKTLEN];
434	char rname[MAXNAMELEN+1];
435	char secret[MAXSECRETLEN+1];
436
437	if ((cs->flags & (LOWERUP | AUTH_STARTED)) != (LOWERUP | AUTH_STARTED))
438		return;		/* not ready */
439	if (len < 2 || len < pkt[0] + 1)
440		return;		/* too short */
441	clen = pkt[0];
442	nlen = len - (clen + 1);
443
444	/* Null terminate and clean remote name. */
445	slprintf(rname, sizeof(rname), "%.*v", nlen, pkt + clen + 1);
446
447	/* Microsoft doesn't send their name back in the PPP packet */
448	if (explicit_remote || (remote_name[0] != 0 && rname[0] == 0))
449		strlcpy(rname, remote_name, sizeof(rname));
450
451	/* get secret for authenticating ourselves with the specified host */
452	if (!get_secret(0, cs->name, rname, secret, &secret_len, 0)) {
453		secret_len = 0;	/* assume null secret if can't find one */
454		warn("No CHAP secret found for authenticating us to %q", rname);
455	}
456
457	p = response;
458	MAKEHEADER(p, PPP_CHAP);
459	p += CHAP_HDRLEN;
460
461	cs->digest->make_response(p, id, cs->name, pkt,
462				  secret, secret_len, cs->priv);
463	memset(secret, 0, secret_len);
464
465	clen = *p;
466	nlen = strlen(cs->name);
467	memcpy(p + clen + 1, cs->name, nlen);
468
469	p = response + PPP_HDRLEN;
470	len = CHAP_HDRLEN + clen + 1 + nlen;
471	p[0] = CHAP_RESPONSE;
472	p[1] = id;
473	p[2] = len >> 8;
474	p[3] = len;
475
476	output(0, response, PPP_HDRLEN + len);
477}
478
479static void
480chap_handle_status(struct chap_client_state *cs, int code, int id,
481		   unsigned char *pkt, int len)
482{
483	const char *msg = NULL;
484
485	if ((cs->flags & (AUTH_DONE|AUTH_STARTED|LOWERUP))
486	    != (AUTH_STARTED|LOWERUP))
487		return;
488	cs->flags |= AUTH_DONE;
489
490	if (code == CHAP_SUCCESS) {
491		/* used for MS-CHAP v2 mutual auth, yuck */
492		if (cs->digest->check_success != NULL) {
493			if (!(*cs->digest->check_success)(pkt, len, cs->priv))
494				code = CHAP_FAILURE;
495		} else
496			msg = "CHAP authentication succeeded";
497	} else {
498		if (cs->digest->handle_failure != NULL)
499			(*cs->digest->handle_failure)(pkt, len);
500		else
501			msg = "CHAP authentication failed";
502	}
503	if (msg) {
504		if (len > 0)
505			info("%s: %.*v", msg, len, pkt);
506		else
507			info("%s", msg);
508	}
509	if (code == CHAP_SUCCESS)
510		auth_withpeer_success(0, PPP_CHAP, cs->digest->code);
511	else {
512		cs->flags |= AUTH_FAILED;
513		error("CHAP authentication failed");
514		auth_withpeer_fail(0, PPP_CHAP);
515	}
516}
517
518static void
519chap_input(int unit, unsigned char *pkt, int pktlen)
520{
521	struct chap_client_state *cs = &client;
522	struct chap_server_state *ss = &server;
523	unsigned char code, id;
524	int len;
525
526	if (pktlen < CHAP_HDRLEN)
527		return;
528	GETCHAR(code, pkt);
529	GETCHAR(id, pkt);
530	GETSHORT(len, pkt);
531	if (len < CHAP_HDRLEN || len > pktlen)
532		return;
533	len -= CHAP_HDRLEN;
534
535	switch (code) {
536	case CHAP_CHALLENGE:
537		chap_respond(cs, id, pkt, len);
538		break;
539	case CHAP_RESPONSE:
540		chap_handle_response(ss, id, pkt, len);
541		break;
542	case CHAP_FAILURE:
543	case CHAP_SUCCESS:
544		chap_handle_status(cs, code, id, pkt, len);
545		break;
546	}
547}
548
549static void
550chap_protrej(int unit)
551{
552	struct chap_client_state *cs = &client;
553	struct chap_server_state *ss = &server;
554
555	if (ss->flags & TIMEOUT_PENDING) {
556		ss->flags &= ~TIMEOUT_PENDING;
557		UNTIMEOUT(chap_timeout, ss);
558	}
559	if (ss->flags & AUTH_STARTED) {
560		ss->flags = 0;
561		auth_peer_fail(0, PPP_CHAP);
562	}
563	if ((cs->flags & (AUTH_STARTED|AUTH_DONE)) == AUTH_STARTED) {
564		cs->flags &= ~AUTH_STARTED;
565		error("CHAP authentication failed due to protocol-reject");
566		auth_withpeer_fail(0, PPP_CHAP);
567	}
568}
569
570/*
571 * chap_print_pkt - print the contents of a CHAP packet.
572 */
573static char *chap_code_names[] = {
574	"Challenge", "Response", "Success", "Failure"
575};
576
577static int
578chap_print_pkt(unsigned char *p, int plen,
579	       void (*printer) __P((void *, char *, ...)), void *arg)
580{
581	int code, id, len;
582	int clen, nlen;
583	unsigned char x;
584
585	if (plen < CHAP_HDRLEN)
586		return 0;
587	GETCHAR(code, p);
588	GETCHAR(id, p);
589	GETSHORT(len, p);
590	if (len < CHAP_HDRLEN || len > plen)
591		return 0;
592
593	if (code >= 1 && code <= sizeof(chap_code_names) / sizeof(char *))
594		printer(arg, " %s", chap_code_names[code-1]);
595	else
596		printer(arg, " code=0x%x", code);
597	printer(arg, " id=0x%x", id);
598	len -= CHAP_HDRLEN;
599	switch (code) {
600	case CHAP_CHALLENGE:
601	case CHAP_RESPONSE:
602		if (len < 1)
603			break;
604		clen = p[0];
605		if (len < clen + 1)
606			break;
607		++p;
608		nlen = len - clen - 1;
609		printer(arg, " <");
610		for (; clen > 0; --clen) {
611			GETCHAR(x, p);
612			printer(arg, "%.2x", x);
613		}
614		printer(arg, ">, name = ");
615		print_string((char *)p, nlen, printer, arg);
616		break;
617	case CHAP_FAILURE:
618	case CHAP_SUCCESS:
619		printer(arg, " ");
620		print_string((char *)p, len, printer, arg);
621		break;
622	default:
623		for (clen = len; clen > 0; --clen) {
624			GETCHAR(x, p);
625			printer(arg, " %.2x", x);
626		}
627	}
628
629	return len + CHAP_HDRLEN;
630}
631
632struct protent chap_protent = {
633	PPP_CHAP,
634	chap_init,
635	chap_input,
636	chap_protrej,
637	chap_lowerup,
638	chap_lowerdown,
639	NULL,		/* open */
640	NULL,		/* close */
641	chap_print_pkt,
642	NULL,		/* datainput */
643	1,		/* enabled_flag */
644	"CHAP",		/* name */
645	NULL,		/* data_name */
646	chap_option_list,
647	NULL,		/* check_options */
648};
649