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