/*
* 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_lcp.h"
#include "ppp/ppp_node.h"
#include "ppp/ppp_engine.h"
#include "ppp/ppp_bundle.h"
#include "ppp/ppp_channel.h"
#include "ppp/ppp_link.h"
#define LINK_MTYPE "ppp_link"
#define PKTBUFLEN 4096
#define LINK_LATENCY 100 /* arbitrary fixed value */
#define LINK_BANDWIDTH 100 /* arbitrary fixed value */
#define AUTH_TIMEOUT 20 /* time limit for auth phase */
#define WINXP_PPTP_HACK(x) ((x) - 20) /* winxp pptp stupidity hack */
/*
* Authorization info for one direction
*/
struct ppp_link_auth {
struct ppp_link *link; /* back pointer */
u_int16_t proto; /* auth protocol number */
const struct ppp_auth_type *type; /* auth type negotiated */
void *arg; /* auth object in progress */
struct ppp_auth_cred cred; /* auth credentials */
struct ppp_auth_resp resp; /* auth response */
union ppp_auth_mppe mppe; /* mppe keys from ms-chap */
struct paction *action;/* auth acquire/check action */
};
/*
* PPP link structure
*/
struct ppp_link {
enum ppp_link_state state; /* link state */
struct ppp_log *log; /* log */
struct ppp_link_config conf; /* link configuration */
struct ppp_engine *engine; /* ppp engine */
struct ppp_channel *device; /* underlying device */
struct ppp_node *node; /* ng_ppp(4) node */
struct ppp_fsm *lcp; /* lcp fsm */
struct ppp_lcp_req lcp_req; /* lcp negotiation result */
struct ppp_bundle *bundle; /* bundle, if joined */
struct pevent_ctx *ev_ctx; /* event context */
pthread_mutex_t *mutex; /* mutex */
struct pevent *lcp_event; /* lcp fsm event */
struct pevent *dev_event; /* device event */
struct pevent *auth_timer; /* timer for auth phase */
u_char device_up; /* device is up */
struct ppp_link_auth auth[2]; /* authorization info */
u_int16_t link_num; /* ppp node link number */
u_char shutdown; /* normal shutdown */
};
/* Internal functions */
static void ppp_link_join(struct ppp_link *link);
static void ppp_link_unjoin(struct ppp_link *link);
static int ppp_link_get_node(struct ppp_link *link);
static void ppp_link_auth_start(struct ppp_link *link);
static void ppp_link_auth_stop(struct ppp_link *link);
static ppp_node_recv_t ppp_link_node_recv;
static pevent_handler_t ppp_link_device_event;
static pevent_handler_t ppp_link_lcp_event;
static pevent_handler_t ppp_link_auth_timeout;
/* Macro for logging */
#define LOG(sev, fmt, args...) PPP_LOG(link->log, sev, fmt , ## args)
/***********************************************************************
PUBLIC FUNCTIONS
***********************************************************************/
/*
* Create a new PPP link.
*
* The link is not returned. Instead it disappears into the PPP engine.
* The "device" and "log" are destroyed when the link is destroyed.
*/
int
ppp_link_create(struct ppp_engine *engine, struct ppp_channel *device,
struct ppp_link_config *conf, struct ppp_log *log)
{
struct ppp_fsm_instance *inst = NULL;
struct ppp_lcp_config lcp_conf;
struct ppp_fsm *lcp = NULL;
struct ppp_link *link;
int esave;
int i;
int j;
/* Create new link structure */
if ((link = MALLOC(LINK_MTYPE, sizeof(*link))) == NULL)
return (-1);
memset(link, 0, sizeof(*link));
link->state = PPP_LINK_DOWN;
link->engine = engine;
link->ev_ctx = ppp_engine_get_ev_ctx(engine);
link->mutex = ppp_engine_get_mutex(engine);
link->device = device;
link->conf = *conf;
link->log = log;
for (i = 0; i < 2; i++)
link->auth[i].link = link;
/* Derive LCP configuration from link configuration and device info */
memset(&lcp_conf, 0, sizeof(lcp_conf));
lcp_conf.max_mru[PPP_SELF] = conf->max_self_mru;
lcp_conf.min_mru[PPP_PEER] = conf->min_peer_mru;
lcp_conf.accm = ppp_channel_is_async(device) ? 0x0a000000 : ~0;
if (ppp_channel_get_acfcomp(device)) {
lcp_conf.acfcomp[PPP_SELF] = 1;
lcp_conf.acfcomp[PPP_PEER] = 1;
}
if (ppp_channel_get_pfcomp(device)) {
lcp_conf.pfcomp[PPP_SELF] = 1;
lcp_conf.pfcomp[PPP_PEER] = 1;
}
for (i = 0; i < PPP_AUTH_MAX; i++) {
for (j = 0; j < 2; j++) {
if ((conf->auth.allow[j] & (1 << i)) != 0)
lcp_conf.auth[j][i] = 1;
}
}
lcp_conf.eid = conf->eid;
if (conf->multilink) {
lcp_conf.max_mrru[PPP_SELF] = conf->max_self_mrru;
lcp_conf.min_mrru[PPP_PEER] = conf->min_peer_mrru;
lcp_conf.multilink[PPP_SELF] = 1;
lcp_conf.multilink[PPP_PEER] = 1;
lcp_conf.shortseq[PPP_SELF] = 1;
lcp_conf.shortseq[PPP_PEER] = 1;
}
/* Create ppp node object */
if (ppp_link_get_node(link) == -1)
goto fail;
/* Create a new LCP FSM for this link */
if ((inst = ppp_lcp_create(&lcp_conf)) == NULL) {
LOG(LOG_ERR, "failed to create LCP: %m");
goto fail;
}
if ((link->lcp = ppp_fsm_create(link->ev_ctx,
link->mutex, inst, link->log)) == NULL) {
LOG(LOG_ERR, "failed to create LCP: %m");
goto fail;
}
inst = NULL;
/* Listen for device events */
if (pevent_register(link->ev_ctx, &link->dev_event, PEVENT_RECURRING,
link->mutex, ppp_link_device_event, link, PEVENT_MESG_PORT,
ppp_channel_get_outport(link->device)) == -1) {
LOG(LOG_ERR, "%s: %m", "adding read event");
goto fail;
}
/* Listen for LCP events */
if (pevent_register(link->ev_ctx, &link->lcp_event, PEVENT_RECURRING,
link->mutex, ppp_link_lcp_event, link, PEVENT_MESG_PORT,
ppp_fsm_get_outport(link->lcp)) == -1) {
LOG(LOG_ERR, "%s: %m", "adding read event");
goto fail;
}
/* Notify engine of new link */
if (ppp_engine_add_link(engine, link) == -1) {
LOG(LOG_ERR, "failed to add link: %m");
goto fail;
}
/* Start LCP negotiations (whenver link comes up) */
ppp_fsm_input(link->lcp, FSM_INPUT_OPEN);
/* Done */
return (0);
fail:
/* Clean up after failure */
esave = errno;
pevent_unregister(&link->dev_event);
pevent_unregister(&link->lcp_event);
ppp_node_destroy(&link->node);
ppp_node_destroy(&link->node);
ppp_fsm_destroy(&lcp);
if (inst != NULL)
(*inst->type->destroy)(inst);
FREE(LINK_MTYPE, link);
errno = esave;
return (-1);
}
/*
* Destroy a link.
*/
void
ppp_link_destroy(struct ppp_link **linkp)
{
struct ppp_link *const link = *linkp;
int r;
/* Sanity check */
if (link == NULL)
return;
*linkp = NULL;
/* Avoid recursion */
if (link->shutdown)
return;
link->shutdown = 1;
/* Acquire lock */
r = pthread_mutex_lock(link->mutex);
assert(r == 0);
/* Stop authentication (if any) */
ppp_link_auth_stop(link);
/* Disconnect from bundle or engine */
ppp_bundle_unjoin(&link->bundle, link);
ppp_engine_del_link(link->engine, link);
/* Destroy link */
r = pthread_mutex_unlock(link->mutex);
assert(r == 0);
ppp_fsm_destroy(&link->lcp);
pevent_unregister(&link->dev_event);
pevent_unregister(&link->lcp_event);
ppp_node_destroy(&link->node);
ppp_channel_destroy(&link->device);
ppp_log_close(&link->log);
FREE(LINK_MTYPE, link);
}
/*
* Close a link.
*/
void
ppp_link_close(struct ppp_link *link)
{
ppp_link_auth_stop(link);
ppp_fsm_input(link->lcp, FSM_INPUT_CLOSE);
}
/*
* Get the device associated with a link.
*/
struct ppp_channel *
ppp_link_get_device(struct ppp_link *link)
{
return (link->device);
}
/*
* Get the link's origination (PPP_SELF or PPP_PEER) which
* is simply inherited from the underlying device.
*/
int
ppp_link_get_origination(struct ppp_link *link)
{
return (ppp_channel_get_origination(link->device));
}
/*
* Get link state.
*/
enum ppp_link_state
ppp_link_get_state(struct ppp_link *link)
{
return (link->state);
}
/*
* Get bundle associated with link, if any.
*/
struct ppp_bundle *
ppp_link_get_bundle(struct ppp_link *link)
{
if (link->bundle == NULL)
errno = ENXIO;
return (link->bundle);
}
/*
* Get LCP request state.
*/
void
ppp_link_get_lcp_req(struct ppp_link *link, struct ppp_lcp_req *req)
{
ppp_lcp_get_req(link->lcp, req);
}
/*
* Get link authorization name.
*
* Note: we reverse the sense of 'dir' because we want PPP_SELF to
* mean my authname to peer, which is the opposite of the way that
* authorization is negotiated, i.e., PPP_SELF is peer's auth to me.
*/
const char *
ppp_link_get_authname(struct ppp_link *link, int dir)
{
struct ppp_link_auth *const auth = &link->auth[!dir];
if (auth->type == NULL)
return ("");
switch (auth->type->index) {
case PPP_AUTH_PAP:
return (auth->cred.u.pap.name);
case PPP_AUTH_CHAP_MSV1:
case PPP_AUTH_CHAP_MSV2:
case PPP_AUTH_CHAP_MD5:
return (auth->cred.u.chap.name);
default:
return ("");
}
}
/*
* Get endpoint ID.
*/
void
ppp_link_get_eid(struct ppp_link *link, int dir, struct ppp_eid *eid)
{
struct ppp_lcp_req req;
dir &= 1;
ppp_lcp_get_req(link->lcp, &req);
*eid = req.eid[dir];
}
void
ppp_link_get_mppe(struct ppp_link *link, int dir, union ppp_auth_mppe *mppe)
{
struct ppp_link_auth *const auth = &link->auth[dir & 1];
memcpy(mppe, &auth->mppe, sizeof(*mppe));
}
/*
* Ouput a packet on the link.
*/
void
ppp_link_write(struct ppp_link *link,
u_int16_t proto, const void *data, size_t len)
{
int rtn;
/* Drop packet if channel is down */
if (!link->device_up)
return;
/* Write packet */
if (link->bundle != NULL) {
rtn = ppp_bundle_write(link->bundle,
link->link_num, proto, data, len);
} else {
rtn = ppp_node_write(link->node,
link->link_num, proto, data, len);
}
if (rtn == -1) {
LOG(LOG_ERR, "%s: %m", "error writing to bypass");
ppp_link_close(link);
}
}
/***********************************************************************
LCP EVENT HANDLER
***********************************************************************/
static void
ppp_link_lcp_event(void *arg)
{
struct ppp_link *link = arg;
struct mesg_port *const outport = ppp_fsm_get_outport(link->lcp);
struct ppp_fsm_output *output;
/* Read and handle all FSM events */
while ((output = mesg_port_get(outport, 0)) != NULL) {
/* Check it out */
switch (output->type) {
case FSM_OUTPUT_OPEN:
ppp_channel_open(link->device);
break;
case FSM_OUTPUT_CLOSE:
ppp_channel_close(link->device);
break;
case FSM_OUTPUT_UP:
{
struct ng_ppp_node_conf conf;
struct ng_ppp_link_conf *const lconf = &conf.links[0];
/* Get ng_ppp(4) node configuration */
if (ppp_node_get_config(link->node, &conf) == -1) {
LOG(LOG_ERR, "can't configure node: %m");
ppp_link_close(link);
break;
}
/* Update with negotiated LCP parameters */
ppp_lcp_get_req(link->lcp, &link->lcp_req);
lconf->enableProtoComp = link->lcp_req.pfcomp[PPP_PEER];
lconf->enableACFComp = link->lcp_req.acfcomp[PPP_PEER];
lconf->mru = MIN(
WINXP_PPTP_HACK(link->lcp_req.mru[PPP_PEER]),
ppp_channel_get_mtu(link->device));
lconf->latency = LINK_LATENCY;
lconf->bandwidth = LINK_BANDWIDTH;
if (ppp_node_set_config(link->node, &conf) == -1) {
LOG(LOG_ERR, "can't configure node: %m");
ppp_link_close(link);
break;
}
/* Begin authentication phase */
link->state = PPP_LINK_AUTH;
ppp_link_auth_start(link);
break;
}
case FSM_OUTPUT_DOWN:
link->state = PPP_LINK_DOWN;
ppp_link_auth_stop(link);
if (link->bundle != NULL) {
/* Leave our bundle */
assert(link->node == NULL);
ppp_bundle_unjoin(&link->bundle, link);
/* Create a new node for this link */
if (ppp_link_get_node(link) == -1) {
ppp_link_close(link);
break;
}
}
break;
case FSM_OUTPUT_DATA:
ppp_link_write(link, PPP_PROTO_LCP,
output->u.data.data, output->u.data.length);
break;
case FSM_OUTPUT_PROTOREJ:
{
/* Log it */
LOG(LOG_NOTICE,
"peer rejected protocol 0x%04x", output->u.proto);
/* If fatal, shut down, else report to bundle */
switch (output->u.proto) {
case PPP_PROTO_LCP:
case PPP_PROTO_CHAP:
case PPP_PROTO_PAP:
case PPP_PROTO_MP:
ppp_fsm_input(link->lcp,
FSM_INPUT_RECD_PROTOREJ, output->u.proto);
break;
default:
if (link->bundle != NULL) {
ppp_bundle_protorej(link->bundle,
output->u.proto);
}
break;
}
break;
}
case FSM_OUTPUT_DEAD:
LOG(LOG_INFO, "LCP is dead: %s",
ppp_fsm_reason_str(output));
if (link->bundle != NULL)
ppp_link_unjoin(link);
ppp_fsm_free_output(output);
ppp_link_destroy(&link);
return;
}
/* Free output */
ppp_fsm_free_output(output);
}
}
/***********************************************************************
DEVICE EVENT HANDLER
***********************************************************************/
static void
ppp_link_device_event(void *arg)
{
struct ppp_link *const link = arg;
struct mesg_port *const outport = ppp_channel_get_outport(link->device);
struct ppp_channel_output *output;
/* Handle channel events */
while ((output = mesg_port_get(outport, 0)) != NULL) {
/* Check event */
switch (output->type) {
case PPP_CHANNEL_OUTPUT_UP:
LOG(LOG_INFO, "device layer is up");
ppp_fsm_input(link->lcp, FSM_INPUT_UP);
link->device_up = 1;
break;
case PPP_CHANNEL_OUTPUT_DOWN_FATAL:
case PPP_CHANNEL_OUTPUT_DOWN_NONFATAL:
LOG(LOG_INFO, "device layer is down: %s", output->info);
ppp_fsm_input(link->lcp,
output->type == PPP_CHANNEL_OUTPUT_DOWN_FATAL ?
FSM_INPUT_DOWN_FATAL : FSM_INPUT_DOWN_NONFATAL);
link->device_up = 0;
break;
}
/* Free channel output */
ppp_channel_free_output(link->device, output);
}
}
/***********************************************************************
PPP NODE OUTPUT HANDLER
***********************************************************************/
/*
* Handle data received from the node's bypass hook.
*/
void
ppp_link_recv_bypass(struct ppp_link *link,
u_int16_t proto, u_char *data, size_t len)
{
LOG(LOG_DEBUG + 1, "rec'd proto 0x%04x %u bytes", proto, len);
switch (proto) {
case PPP_PROTO_LCP:
ppp_fsm_input(link->lcp, FSM_INPUT_DATA, data, len);
break;
case PPP_PROTO_PAP:
case PPP_PROTO_CHAP:
{
int i;
for (i = 0; i < 2; i++) {
struct ppp_link_auth *const auth = &link->auth[i];
if (auth->type == NULL
|| auth->arg == NULL
|| auth->proto != proto)
continue;
(*auth->type->input)(auth->arg, i, data, len);
}
break;
}
case PPP_PROTO_MP:
case PPP_PROTO_IPCP:
case PPP_PROTO_IP:
case PPP_PROTO_VJCOMP:
case PPP_PROTO_VJUNCOMP:
case PPP_PROTO_CCP:
case PPP_PROTO_COMPD:
break;
default: /* send a protocol-reject */
ppp_fsm_input(link->lcp,
FSM_INPUT_XMIT_PROTOREJ, proto, data, len);
break;
}
}
static void
ppp_link_node_recv(void *arg, u_int link_num,
u_int16_t proto, u_char *data, size_t len)
{
struct ppp_link *const link = arg;
assert(link_num == 0);
ppp_link_recv_bypass(link, proto, data, len);
}
/***********************************************************************
AUTHORIZATION PHASE
***********************************************************************/
/*
* Begin link authorization.
*/
static void
ppp_link_auth_start(struct ppp_link *link)
{
int i;
/* Get auth types negotiated by LCP */
for (i = 0; i < 2; i++) {
link->auth[i].type = (link->lcp_req.auth[i] != PPP_AUTH_NONE) ?
ppp_auth_by_index(link->lcp_req.auth[i]) : NULL;
}
LOG(LOG_DEBUG, "auth required: self=%s peer=%s",
link->auth[PPP_PEER].type != NULL ?
link->auth[PPP_PEER].type->name : "None",
link->auth[PPP_SELF].type != NULL ?
link->auth[PPP_SELF].type->name : "None");
/* If no auth required, skip it */
if (link->auth[PPP_SELF].type == NULL
&& link->auth[PPP_PEER].type == NULL) {
ppp_link_join(link);
return;
}
/* Start auth timer */
pevent_unregister(&link->auth_timer);
if (pevent_register(link->ev_ctx, &link->auth_timer, 0,
link->mutex, ppp_link_auth_timeout, link, PEVENT_TIME,
AUTH_TIMEOUT * 1000) == -1) {
LOG(LOG_ERR, "%s: %m", "pevent_register");
ppp_link_close(link);
return;
}
/* Start authorization in each direction */
for (i = 0; i < 2; i++) {
struct ppp_link_auth *const auth = &link->auth[i];
if (auth->type == NULL)
continue;
if ((auth->arg = (*auth->type->start)(link->ev_ctx, link,
link->mutex, i, &auth->proto, ppp_log_dup(link->log)))
== NULL) {
LOG(LOG_ERR, "failed to initialize authorization");
ppp_link_close(link);
return;
}
}
}
/*
* Stop link authorization.
*/
static void
ppp_link_auth_stop(struct ppp_link *link)
{
int i;
/* Stop auth timer */
pevent_unregister(&link->auth_timer);
/* Stop auth in both directions */
for (i = 0; i < 2; i++) {
struct ppp_link_auth *const auth = &link->auth[i];
/* Kill any threads */
paction_cancel(&auth->action);
/* Clean up authorization code */
if (auth->arg != NULL) {
(*auth->type->cancel)(auth->arg);
auth->arg = NULL;
}
}
}
/*
* Authorization failed to happen within the alloted time.
*/
static void
ppp_link_auth_timeout(void *arg)
{
struct ppp_link *const link = arg;
pevent_unregister(&link->auth_timer);
LOG(LOG_ERR, "authorization timed out");
ppp_link_close(link);
}
/***********************************************************************
AUTHORIZATION CALLBACKS
***********************************************************************/
#define AUTHINFO_MTYPE "ppp_link.authinfo"
/* Authorization acquire/check state */
struct ppp_link_auth_info {
struct ppp_link *link; /* link being authorized */
struct ppp_auth_cred cred; /* auth credentials */
struct ppp_auth_resp resp; /* auth response */
ppp_link_auth_finish_t *finish;/* auth finish routine */
int rtn; /* return value from user */
int error; /* saved value of 'errno' */
};
static paction_handler_t ppp_link_auth_acquire_main;
static paction_finish_t ppp_link_auth_acquire_finish;
static paction_handler_t ppp_link_auth_check_main;
static paction_finish_t ppp_link_auth_check_finish;
/*
* Acquire or check authorization credentials.
*
* This action is done in a separate thread.
*/
int
ppp_link_authorize(struct ppp_link *link, int dir,
const struct ppp_auth_cred *cred, ppp_link_auth_finish_t *authfinish)
{
struct ppp_link_auth *const auth = &link->auth[dir];
struct ppp_link_auth_info *authinfo;
paction_handler_t *handler;
paction_finish_t *finish;
/* Sanity check */
assert(auth->action == NULL);
/* Set up for 'acquire' or 'check' */
if (dir == PPP_PEER) {
if (link->conf.auth.meth->acquire == NULL) {
LOG(LOG_ERR, "no method provided for %s credentials",
"acquiring");
return (-1);
}
handler = ppp_link_auth_acquire_main;
finish = ppp_link_auth_acquire_finish;
} else {
if (link->conf.auth.meth->check == NULL) {
LOG(LOG_ERR, "no method provided for %s credentials",
"checking");
return (-1);
}
handler = ppp_link_auth_check_main;
finish = ppp_link_auth_check_finish;
}
/* Create auth info */
if ((authinfo = MALLOC(AUTHINFO_MTYPE, sizeof(*authinfo))) == NULL) {
LOG(LOG_ERR, "%s: %m", "malloc");
return (-1);
}
memset(authinfo, 0, sizeof(*authinfo));
authinfo->link = link;
authinfo->cred = *cred;
authinfo->finish = authfinish;
/* Initiate authorization action */
if (paction_start(&auth->action, link->mutex,
handler, finish, authinfo) == -1) {
LOG(LOG_ERR, "%s: %m", "paction_start");
FREE(AUTHINFO_MTYPE, authinfo);
return (-1);
}
/* Done */
return (0);
}
/*
* Acquire authorization credentials.
*
* The mutex is NOT locked when this is called.
*/
static void
ppp_link_auth_acquire_main(void *arg)
{
struct ppp_link_auth_info *const authinfo = arg;
struct ppp_link *const link = authinfo->link;
/* Acquire credentials */
authinfo->rtn = (*link->conf.auth.meth->acquire)(link,
&authinfo->cred, &authinfo->resp);
authinfo->error = errno;
}
/*
* Finish acquiring authorization credentials.
*
* The mutex is locked when this is called unless 'canceled' is true.
*/
static void
ppp_link_auth_acquire_finish(void *arg, int canceled)
{
struct ppp_link_auth_info *const authinfo = arg;
struct ppp_link *const link = authinfo->link;
struct ppp_link_auth *const auth = &link->auth[PPP_PEER];
/* If canceled, just clean up */
if (canceled)
goto done;
/* If acquiring credentials failed, bail out here */
if (authinfo->rtn != 0) {
if (*authinfo->resp.errmsg == '\0') {
strlcpy(authinfo->resp.errmsg,
strerror(authinfo->error),
sizeof(authinfo->resp.errmsg));
}
LOG(LOG_WARNING, "failed to acquire credentials: %s",
auth->resp.errmsg);
ppp_link_auth_complete(link, PPP_PEER, NULL, NULL);
return;
}
/* Save a copy of credentials */
auth->cred = authinfo->cred;
auth->resp = authinfo->resp;
/* Report credentials back to authorization code */
(*authinfo->finish)(auth->arg, &authinfo->cred, &authinfo->resp);
done:
/* Free authinfo */
FREE(AUTHINFO_MTYPE, authinfo);
}
/*
* Check authorization credentials.
*
* The mutex is NOT locked when this is called.
*/
static void
ppp_link_auth_check_main(void *arg)
{
struct ppp_link_auth_info *const authinfo = arg;
struct ppp_link *const link = authinfo->link;
/* Check credentials */
authinfo->rtn = (*link->conf.auth.meth->check)(link,
&authinfo->cred, &authinfo->resp);
authinfo->error = errno;
}
/*
* Finish checking authorization credentials.
*
* The mutex is locked when this is called.
*/
static void
ppp_link_auth_check_finish(void *arg, int canceled)
{
struct ppp_link_auth_info *const authinfo = arg;
struct ppp_link *const link = authinfo->link;
struct ppp_link_auth *const auth = &link->auth[PPP_SELF];
/* If canceled, just clean up */
if (canceled)
goto done;
/* Save a copy of credentials */
auth->cred = authinfo->cred;
auth->resp = authinfo->resp;
/* Fill in error message to indicate invalid credentials */
if (authinfo->rtn != 0) {
if (*authinfo->resp.errmsg == '\0') {
strlcpy(authinfo->resp.errmsg,
strerror(authinfo->error),
sizeof(authinfo->resp.errmsg));
}
}
/* Report result back to authorization code */
(*authinfo->finish)(auth->arg, &authinfo->cred, &authinfo->resp);
done:
/* Free authinfo */
FREE(AUTHINFO_MTYPE, authinfo);
}
/*
* Determine if an authorization action is already in progress.
*/
int
ppp_link_auth_in_progress(struct ppp_link *link, int dir)
{
struct ppp_link_auth *const auth = &link->auth[dir];
return (auth->action != NULL);
}
/*
* Finish link authorization (in one direction).
*
* A NULL 'cred' indicates failure.
*/
void
ppp_link_auth_complete(struct ppp_link *link, int dir,
const struct ppp_auth_cred *cred, const union ppp_auth_mppe *mppe)
{
struct ppp_link_auth *const auth = &link->auth[dir];
/* Sanity check */
assert(auth->arg != NULL);
/* If auth failed, close link */
if (cred == NULL) {
LOG(LOG_NOTICE, "authorization %s peer failed",
dir == PPP_SELF ? "from" : "to");
ppp_link_close(link);
return;
}
/* Save credentials and MPPE info */
auth->cred = *cred;
if (mppe != NULL)
auth->mppe = *mppe;
/* Destroy auth object for this direction */
(*auth->type->cancel)(auth->arg);
auth->arg = NULL;
/* If other direction still active, let it finish */
if (link->auth[!dir].arg != NULL)
return;
/* Move to the 'UP' state */
ppp_link_auth_stop(link);
ppp_link_join(link);
}
/*
* Get this link's authorization configuration.
*/
const struct ppp_auth_config *
ppp_link_auth_get_config(struct ppp_link *link)
{
return (&link->conf.auth);
}
/*
* Get this link's authorization type in one direction.
*/
const struct ppp_auth_type *
ppp_link_get_auth(struct ppp_link *link, int dir)
{
dir &= 1;
return (link->auth[dir].type);
}
/*
* Get this link's log
*/
struct ppp_log *
ppp_link_get_log(struct ppp_link *link)
{
return (link->log);
}
/***********************************************************************
BUNDLE OPERATIONS
***********************************************************************/
/*
* The link FSM has reached the OPENED state and authentication
* was successful, so join a bundle.
*/
static void
ppp_link_join(struct ppp_link *link)
{
assert(link->bundle == NULL);
if ((link->bundle = ppp_engine_join(link->engine,
link, &link->node, &link->link_num)) == NULL) {
ppp_link_close(link);
return;
}
link->state = PPP_LINK_UP;
}
/*
* Link has left the OPENED state, so leave bundle (if any).
*/
static void
ppp_link_unjoin(struct ppp_link *link)
{
ppp_bundle_unjoin(&link->bundle, link); /* ok if bundle null */
}
/***********************************************************************
INTERNAL FUNCTIONS
***********************************************************************/
/*
* Acquire an ng_ppp(4) node for this link to use.
*/
static int
ppp_link_get_node(struct ppp_link *link)
{
const char *node;
const char *hook;
/* Sanity check */
if (link->node != NULL)
return (0);
/* Get new node */
if ((link->node = ppp_node_create(link->ev_ctx,
link->mutex, ppp_log_dup(link->log))) == NULL) {
LOG(LOG_ERR, "%s: %m", "creating ppp node");
return (-1);
}
/* Connect device to node */
if ((node = ppp_channel_get_node(link->device)) == NULL
|| (hook = ppp_channel_get_hook(link->device)) == NULL) {
LOG(LOG_ERR, "channel is not a device");
ppp_node_destroy(&link->node);
return (-1);
}
if (ppp_node_connect(link->node, 0, node, hook) == -1) {
LOG(LOG_ERR, "%s: %m", "connecting device to node");
ppp_node_destroy(&link->node);
return (-1);
}
/* Receive all node bypass packets */
ppp_node_set_recv(link->node, ppp_link_node_recv, link);
/* Done */
return (0);
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>