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