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