File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libpdel / ppp / ppp_ccp.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:25:53 2012 UTC (12 years, 4 months ago) by misho
Branches: libpdel, MAIN
CVS tags: v0_5_3, HEAD
libpdel


/*
 * Copyright (c) 2001-2002 Packet Design, LLC.
 * All rights reserved.
 * 
 * Subject to the following obligations and disclaimer of warranty,
 * use and redistribution of this software, in source or object code
 * forms, with or without modifications are expressly permitted by
 * Packet Design; provided, however, that:
 * 
 *    (i)  Any and all reproductions of the source or object code
 *         must include the copyright notice above and the following
 *         disclaimer of warranties; and
 *    (ii) No rights are granted, in any manner or form, to use
 *         Packet Design trademarks, including the mark "PACKET DESIGN"
 *         on advertising, endorsements, or otherwise except as such
 *         appears in the above copyright notice or in the software.
 * 
 * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
 * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
 * OR NON-INFRINGEMENT.  PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
 * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
 * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
 * RELIABILITY OR OTHERWISE.  IN NO EVENT SHALL PACKET DESIGN BE
 * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
 * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
 * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
 * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Author: Archie Cobbs <archie@freebsd.org>
 */

#include "ppp/ppp_defs.h"
#include "ppp/ppp_log.h"
#include "ppp/ppp_fsm_option.h"
#include "ppp/ppp_fsm.h"
#include "ppp/ppp_auth.h"
#include "ppp/ppp_node.h"
#include "ppp/ppp_ccp.h"

#include <netgraph/ng_mppc.h>

#ifndef MPPE_56
#define	MPPE_56		0x00000080
#undef MPPE_BITS
#define	MPPE_BITS	0x000000e0
#endif

/*
 * CCP FSM
 *
 * XXX This is hard coded to only support MPPC/MPPE compression.
 * XXX It should be generalized to support multiple PPP compression types.
 */

#define MPPC_SUPPORTED		(MPPC_BIT | MPPE_BITS | MPPE_STATELESS)

/* Memory type */
#define CCP_MTYPE		"ccp"

/* CCP compression types */
enum ccp_type {
	CCP_OPT_OUI		=0,	/* oui */
	CCP_OPT_PRED1		=1,	/* predictor type 1 */
	CCP_OPT_PRED2		=2,	/* predictor type 2 */
	CCP_OPT_PUDDLE		=3,	/* puddle jumper */
	CCP_OPT_HWPPC		=16,	/* hewlett-packard ppc */
	CCP_OPT_STAC		=17,	/* stac electronics lzs */
	CCP_OPT_MPPC		=18,	/* microsoft mppc/mppe */
	CCP_OPT_GAND		=19,	/* gandalf fza */
	CCP_OPT_V42BIS		=20,	/* v.42bis compression */
	CCP_OPT_BSD		=21,	/* bsd lzw compress */
	CCP_OPT_DEFLATE		=24,	/* gzip "deflate" compression */
};

/* Supported and required FSM codes */
#define CCP_SUPPORTED_CODES		\
	  (1 << FSM_CODE_CONFIGREQ)	\
	| (1 << FSM_CODE_CONFIGACK)	\
	| (1 << FSM_CODE_CONFIGNAK)	\
	| (1 << FSM_CODE_CONFIGREJ)	\
	| (1 << FSM_CODE_TERMREQ)	\
	| (1 << FSM_CODE_TERMACK)	\
	| (1 << FSM_CODE_CODEREJ)	\
	| (1 << FSM_CODE_RESETREQ)	\
	| (1 << FSM_CODE_RESETACK)
#define CCP_REQUIRED_CODES		\
	  (1 << FSM_CODE_CONFIGREQ)	\
	| (1 << FSM_CODE_CONFIGACK)	\
	| (1 << FSM_CODE_CONFIGNAK)	\
	| (1 << FSM_CODE_CONFIGREJ)	\
	| (1 << FSM_CODE_TERMREQ)	\
	| (1 << FSM_CODE_TERMACK)	\
	| (1 << FSM_CODE_CODEREJ)	\
	| (1 << FSM_CODE_RESETREQ)	\
	| (1 << FSM_CODE_RESETACK)

/* FSM options descriptors */
static opt_pr_t	ppp_cpp_pr_mppc;

static const	struct ppp_fsm_optdesc ccp_opt_desc[] = {
	{ "OUI",	CCP_OPT_OUI,	0, 255,	0,	NULL },
	{ "Predictor-1",CCP_OPT_PRED1,	0, 255,	0,	NULL },
	{ "Predictor-2",CCP_OPT_PRED2,	0, 255,	0,	NULL },
	{ "Puddle",	CCP_OPT_PUDDLE,	0, 255,	0,	NULL },
	{ "HWPPC",	CCP_OPT_HWPPC,	0, 255,	0,	NULL },
	{ "Stac",	CCP_OPT_STAC,	0, 255,	0,	NULL },
	{ "MPPC/MPPE",	CCP_OPT_MPPC,	4, 4,	1,	ppp_cpp_pr_mppc },
	{ "Gandalf",	CCP_OPT_GAND,	0, 255,	0,	NULL },
	{ "v.42bis",	CCP_OPT_V42BIS,	0, 255,	0,	NULL },
	{ "BSD-LZW",	CCP_OPT_BSD,	0, 255,	0,	NULL },
	{ "Deflate",	CCP_OPT_DEFLATE,0, 255,	0,	NULL },
	{ NULL,		0,		0, 0,	0,	NULL }
};

/* FSM type for CCP */
static ppp_fsm_type_destroy_t		ppp_ccp_destroy;
static ppp_fsm_type_build_conf_req_t	ppp_ccp_build_conf_req;
static ppp_fsm_type_recv_conf_req_t	ppp_ccp_recv_conf_req;
static ppp_fsm_type_recv_conf_rej_t	ppp_ccp_recv_conf_rej;
static ppp_fsm_type_recv_conf_nak_t	ppp_ccp_recv_conf_nak;
static ppp_fsm_type_recv_reset_req_t	ppp_ccp_recv_reset_req;
static ppp_fsm_type_recv_reset_ack_t	ppp_ccp_recv_reset_ack;

const struct	ppp_fsm_type ppp_fsm_ccp = {
	"CCP",
	PPP_PROTO_CCP,
	CCP_SUPPORTED_CODES,
	CCP_REQUIRED_CODES,
	ccp_opt_desc,
	NULL,
	ppp_ccp_destroy,
	ppp_ccp_build_conf_req,
	ppp_ccp_recv_conf_req,
	ppp_ccp_recv_conf_rej,
	ppp_ccp_recv_conf_nak,
	NULL,
	ppp_ccp_recv_reset_req,
	ppp_ccp_recv_reset_ack,
	NULL
};

/* CCP instance state */
struct ccp {
	struct ppp_fsm_instance	*inst;		/* backpointer to instance */
	struct ppp_ccp_config	conf;		/* initial config */
	struct ppp_ccp_req	req;		/* current request state */
	struct ppp_node		*node;		/* ng_ppp(4) node */
};

/* Internal functions */
static ppp_node_recvmsg_t	ppp_ccp_send_reset_req;

/***********************************************************************
			PUBLIC FUNCTIONS
***********************************************************************/

struct ppp_fsm_instance *
ppp_ccp_create(struct ppp_ccp_config *conf, struct ppp_node *node)
{
	struct ppp_fsm_instance *inst;
	struct ppp_ccp_req *req;
	struct ccp *ccp = NULL;

	/* Construct instance object */
	if ((inst = MALLOC(CCP_MTYPE, sizeof(*inst))) == NULL)
		return (NULL);
	memset(inst, 0, sizeof(*inst));
	inst->type = &ppp_fsm_ccp;

	/* Attach private data */
	if ((ccp = MALLOC(CCP_MTYPE, sizeof(*ccp))) == NULL)
		goto fail;
	memset(ccp, 0, sizeof(*ccp));
	ccp->conf = *conf;
	ccp->node = node;
	ccp->inst = inst;
	inst->arg = ccp;

	/* Initialize local request state */
	req = &ccp->req;
	req->mppc[PPP_SELF] = conf->mppc[PPP_SELF];
	req->mppe40[PPP_SELF] = conf->mppe40[PPP_SELF];
	req->mppe56[PPP_SELF] = conf->mppe56[PPP_SELF];
	req->mppe128[PPP_SELF] = conf->mppe128[PPP_SELF];
	req->mppe_stateless[PPP_SELF] = conf->mppe_stateless[PPP_SELF];

	/* Register handler for reset-req control message from ng_mppc(4) */
	if (ppp_node_set_recvmsg(ccp->node, NGM_MPPC_COOKIE,
	    NGM_MPPC_RESETREQ, ppp_ccp_send_reset_req, ccp) == -1)
		goto fail;

	/* Done */
	return (inst);

fail:
	/* Clean up after failure */
	if (ccp != NULL)
		FREE(CCP_MTYPE, ccp);
	FREE(CCP_MTYPE, inst);
	return (NULL);
}

/*
 * Get CCP request state.
 */
void
ppp_ccp_get_req(struct ppp_fsm *fsm, struct ppp_ccp_req *req)
{
	struct ppp_fsm_instance *const inst = ppp_fsm_get_instance(fsm);
	struct ccp *const ccp = inst->arg;

	assert(inst->type == &ppp_fsm_ccp);
	memcpy(req, &ccp->req, sizeof(*req));
}

/***********************************************************************
			FSM CALLBACKS
***********************************************************************/

static void
ppp_ccp_destroy(struct ppp_fsm_instance *inst)
{
	struct ccp *const ccp = inst->arg;

	ppp_node_set_recvmsg(ccp->node,
	    NGM_MPPC_COOKIE, NGM_MPPC_RESETREQ, NULL, NULL);
	FREE(CCP_MTYPE, ccp);
	FREE(CCP_MTYPE, inst);
}

static int
ppp_ccp_build_conf_req(struct ppp_fsm_instance *fsm,
	struct ppp_fsm_options *opts)
{
	struct ccp *const ccp = (struct ccp *)fsm->arg;
	struct ppp_ccp_req *const req = &ccp->req;
	u_int32_t mppo = 0;

	/* Add MPPC/MPPE requested config options */
	if (req->mppc[PPP_SELF])
		mppo |= MPPC_BIT;
	if (req->mppe40[PPP_SELF])
		mppo |= MPPE_40;
	if (req->mppe56[PPP_SELF])
		mppo |= MPPE_56;
	if (req->mppe128[PPP_SELF])
		mppo |= MPPE_128;
	if (req->mppe_stateless[PPP_SELF])
		mppo |= MPPE_STATELESS;

	/* If no options left to try, don't ask for anything */
	if ((mppo & (MPPC_BIT | MPPE_BITS)) == 0) {
		errno = EINVAL;
		return (-1);
	}

	/* Add option */
	mppo = htonl(mppo);
	if (ppp_fsm_option_add(opts, CCP_OPT_MPPC, 4, &mppo) == -1)
		return (-1);

	/* Done */
	return (0);
}

static int
ppp_ccp_recv_conf_req(struct ppp_fsm_instance *fsm,
	struct ppp_fsm_options *crq, struct ppp_fsm_options *nak,
	struct ppp_fsm_options *rej)
{
	struct ccp *const ccp = (struct ccp *)fsm->arg;
	struct ppp_ccp_config *const conf = &ccp->conf;
	struct ppp_ccp_req *const req = &ccp->req;
	int i;

	/* Initialize peer's request state */
	req->mppc[PPP_PEER] = 0;
	req->mppe40[PPP_PEER] = 0;
	req->mppe56[PPP_PEER] = 0;
	req->mppe128[PPP_PEER] = 0;
	req->mppe_stateless[PPP_PEER] = 0;

	/* Process options */
	for (i = 0; i < crq->num; i++) {
		const struct ppp_fsm_option *const opt = &crq->opts[i];

		switch (opt->type) {
		case CCP_OPT_MPPC:
		    {
			u_int32_t obits;
			u_int32_t bits;

			/* Get requested bits */
			memcpy(&obits, opt->data, 4);
			obits = ntohl(obits);
			bits = obits;

			/* Filter out bits we can't handle */
			bits &= MPPC_SUPPORTED;
			if ((bits & MPPC_BIT) != 0 && !conf->mppc[PPP_PEER])
				bits &= ~MPPC_BIT;
			if ((bits & MPPE_40) != 0 && !conf->mppe40[PPP_PEER])
				bits &= ~MPPE_40;
			if ((bits & MPPE_56) != 0 && !conf->mppe56[PPP_PEER])
				bits &= ~MPPE_56;
			if ((bits & MPPE_128) != 0 && !conf->mppe128[PPP_PEER])
				bits &= ~MPPE_128;
			if ((bits & MPPE_STATELESS) != 0
			    && !conf->mppe_stateless[PPP_PEER])
				bits &= ~MPPE_STATELESS;

			/*
			 * It doesn't really make sense to do MPPE encryption
			 * in only one direction. Also, Win95/98 PPTP can't
			 * handle uni-directional encryption. So if the remote
			 * side doesn't request encryption, try to prompt it.
			 * This is broken wrt. normal PPP negotiation: typical
			 * Microsoft.
			 */
			if ((bits & MPPE_BITS) == 0) {
				if (req->mppe40[PPP_SELF])
					bits |= MPPE_40;
				if (req->mppe56[PPP_SELF])
					bits |= MPPE_56;
				if (req->mppe128[PPP_SELF])
					bits |= MPPE_128;
			}

			/* Make sure we're not left with no options */
			if ((bits & MPPE_BITS) == 0) {
				if (ccp->conf.mppe40[PPP_SELF])
					bits |= MPPE_40;
				if (ccp->conf.mppe56[PPP_SELF])
					bits |= MPPE_56;
				if (ccp->conf.mppe128[PPP_SELF])
					bits |= MPPE_128;
			}

			/* Now choose the strongest encryption available */
			if ((bits & MPPE_128) != 0)
				bits &= ~(MPPE_56|MPPE_40);
			else if ((bits & MPPE_56) != 0)
				bits &= ~(MPPE_128|MPPE_40);
			else if ((bits & MPPE_40) != 0)
				bits &= ~(MPPE_128|MPPE_56);

			/* Send back a nak if there were any changes */
			if (bits != obits) {
				bits = htonl(bits);
				if (ppp_fsm_option_add(nak,
				    opt->type, 4, &bits) == -1)
					return (-1);
				break;
			}

			/* Peer's request is accepted */
			req->mppc[PPP_PEER] = (bits & MPPC_BIT) != 0;
			req->mppe40[PPP_PEER] = (bits & MPPE_40) != 0;
			req->mppe56[PPP_PEER] = (bits & MPPE_56) != 0;
			req->mppe128[PPP_PEER] = (bits & MPPE_128) != 0;
			req->mppe_stateless[PPP_PEER]
			    = (bits & MPPE_STATELESS) != 0;
			break;
		    }
		default:
			goto reject;
		}

		/* OK */
		continue;

reject:
		/* Reject this requested option */
		if (ppp_fsm_option_add(rej,
		    opt->type, opt->len, opt->data) == -1)
			return (-1);
	}

	/* If there were no options, shut down */
	if (crq->num == 0) {
		errno = EINVAL;
		return (-1);
	}

	/* Done */
	return (0);
}

static int
ppp_ccp_recv_conf_rej(struct ppp_fsm_instance *fsm,
	struct ppp_fsm_options *rej)
{
	int i;

	for (i = 0; i < rej->num; i++) {
		const struct ppp_fsm_option *const opt = &rej->opts[i];

		switch (opt->type) {
		case CCP_OPT_MPPC:
			errno = EINVAL;
			return (-1);
		default:
			break;
		}
	}

	/* Done */
	return (0);
}

static int
ppp_ccp_recv_conf_nak(struct ppp_fsm_instance *fsm,
	struct ppp_fsm_options *nak)
{
	struct ccp *const ccp = (struct ccp *)fsm->arg;
	struct ppp_ccp_req *const req = &ccp->req;
	int i;

	for (i = 0; i < nak->num; i++) {
		const struct ppp_fsm_option *const opt = &nak->opts[i];

		switch (opt->type) {
		case CCP_OPT_MPPC:
		    {
			u_int32_t bits;

			memcpy(&bits, opt->data, 4);
			bits = ntohl(bits);

			/* Mask away bits the client didn't like */
			if ((bits & MPPC_BIT) == 0)
				req->mppc[PPP_SELF] = 0;
			if ((bits & MPPE_40) == 0)
				req->mppe40[PPP_SELF] = 0;
			if ((bits & MPPE_56) == 0)
				req->mppe56[PPP_SELF] = 0;
			if ((bits & MPPE_128) == 0)
				req->mppe128[PPP_SELF] = 0;
			if ((bits & MPPE_STATELESS) == 0)
				req->mppe_stateless[PPP_SELF] = 0;

			/* Make sure we're not left with no options */
			if ((bits & MPPE_BITS) == 0) {
				if (ccp->conf.mppe40[PPP_SELF])
					bits |= MPPE_40;
				if (ccp->conf.mppe56[PPP_SELF])
					bits |= MPPE_56;
				if (ccp->conf.mppe128[PPP_SELF])
					bits |= MPPE_128;
			}
			break;
		    }
		default:
			break;
		}
	}

	/* Done */
	return (0);
}

/*
 * Receive a Reset-Req from peer. Relay request to the ng_mppc(4) node.
 */
static void
ppp_ccp_recv_reset_req(struct ppp_fsm_instance *fsm,
	const u_char *data, u_int len)
{
	struct ccp *const ccp = (struct ccp *)fsm->arg;

	ppp_node_send_msg(ccp->node, NG_PPP_HOOK_COMPRESS,
	    NGM_MPPC_COOKIE, NGM_MPPC_RESETREQ, NULL, 0);
}

/*
 * Receive a Reset-Ack.
 */
static void
ppp_ccp_recv_reset_ack(struct ppp_fsm_instance *fsm,
	const u_char *data, u_int len)
{
	return;			/* not used by MPPC/MPPE, just ignore it */
}

/***********************************************************************
			INTERNAL FUNCTIONS
***********************************************************************/

static void
ppp_ccp_send_reset_req(void *arg, struct ng_mesg *msg)
{
	struct ccp *const ccp = arg;
	struct ppp_fsm *const fsm = ccp->inst->fsm;

	ppp_fsm_send_reset_req(fsm, NULL, 0);
}

static void
ppp_cpp_pr_mppc(const struct ppp_fsm_optdesc *desc,
	const struct ppp_fsm_option *opt, char *buf, size_t bmax)
{
	static const struct {
		u_int32_t	bit;
		const char	*name;
	} mppe_bits[] = {
		{ MPPC_BIT,		"MPPC" },
		{ MPPE_BITS,		"MPPE" },
		{ MPPE_40,		"40 bit" },
		{ MPPE_56,		"56 bit" },
		{ MPPE_128,		"128 bit" },
		{ MPPE_STATELESS,	"stateless" },
		{ 0 }
	};
	u_int32_t mppo;
	int first;
	int i;

	if (opt->len < 4) {
		snprintf(buf, bmax, "<truncated>");
		return;
	}
	memcpy(&mppo, opt->data, 4);
	mppo = ntohl(mppo);
	*buf = '\0';
	for (first = 1, i = 0; mppe_bits[i].bit != 0; i++) {
		if ((mppo & mppe_bits[i].bit) != 0) {
			snprintf(buf + strlen(buf), bmax - strlen(buf),
			    "%s%s", first ? "" : ", ", mppe_bits[i].name);
			first = 0;
		}
	}
}


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>