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