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