#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <net/if_dl.h>
#include <stdlib.h>
#include <stdio.h>
#include <syslog.h>
#include <string.h>
#include <event.h>
#include <ctype.h>
#include "npppd.h"
#include "ppp.h"
#include "psm-opt.h"
#define SPACE " \t\r\n"
#include "debugutil.h"
#ifdef LCP_DEBUG
#define LCP_DBG(x) fsm_log x
#define LCP_ASSERT(x) ASSERT(x)
#else
#define LCP_DBG(x)
#define LCP_ASSERT(x)
#endif
#define PROTREJ 0x08
#define ECHOREQ 0x09
#define ECHOREP 0x0a
#define IDENTIFICATION 0x0c
static void lcp_resetci(fsm *);
static void lcp_addci(fsm *, u_char *, int *);
static int lcp_reqci(fsm *, u_char *, int *, int);
static int lcp_ackci(fsm *, u_char *, int);
static int lcp_nakci(fsm *, u_char *, int);
static int lcp_rejci(fsm *, u_char *, int);
static int lcp_cilen(fsm *);
static void lcp_open(fsm *);
static void lcp_down(fsm *);
static void lcp_finished(fsm *);
static int lcp_ext(fsm *, int, int, u_char *, int);
static void lcp_timeout(void *);
static void lcp_reset_timeout(void *);
static int lcp_proxy_recv_ci(fsm *, u_char *, int);
static int lcp_proxy_sent_ci(fsm *, u_char *, int);
static void lcp_load_authconfig(fsm *f);
static struct fsm_callbacks lcp_callbacks = {
lcp_resetci,
lcp_cilen,
lcp_addci,
lcp_ackci,
lcp_nakci,
lcp_rejci,
lcp_reqci,
lcp_open,
lcp_down,
NULL,
lcp_finished,
NULL,
NULL,
lcp_ext,
"lcp"
};
#define NO_AUTH_AGREEABLE(lcp) \
(!psm_opt_is_enabled(lcp, pap) || psm_opt_is_rejected(lcp, pap)) && \
(!psm_opt_is_enabled(lcp, chap) || psm_opt_is_rejected(lcp, chap)) && \
(!psm_opt_is_enabled(lcp, chapms) || psm_opt_is_rejected(lcp, chapms)) &&\
(!psm_opt_is_enabled(lcp, chapms_v2) || psm_opt_is_rejected(lcp, chapms_v2)) && \
(!psm_opt_is_enabled(lcp, eap) || psm_opt_is_rejected(lcp, eap))
void
lcp_init(lcp *_this, npppd_ppp *ppp)
{
struct tunnconf *conf;
fsm_init(&_this->fsm);
_this->fsm.ppp = ppp;
_this->fsm.callbacks = &lcp_callbacks;
_this->fsm.protocol = PPP_PROTO_LCP;
_this->fsm.flags |= OPT_SILENT;
_this->timerctx.ctx = _this;
_this->recv_ress = 0;
_this->recv_reqs = 0;
_this->magic_number = arc4random();
conf = ppp_get_tunnconf(ppp);
PPP_FSM_CONFIG(&_this->fsm, timeouttime, conf->lcp_timeout);
PPP_FSM_CONFIG(&_this->fsm, maxconfreqtransmits,
conf->lcp_max_configure);
PPP_FSM_CONFIG(&_this->fsm, maxtermtransmits,
conf->lcp_max_terminate);
PPP_FSM_CONFIG(&_this->fsm, maxnakloops,
conf->lcp_max_nak_loop);
_this->echo_failures = 0;
if (!conf->lcp_keepalive)
_this->echo_interval = 0;
else {
_this->echo_interval = conf->lcp_keepalive_interval;
_this->echo_retry_interval = conf->lcp_keepalive_retry_interval;
_this->echo_max_retries = conf->lcp_keepalive_max_retries;
}
_this->auth_order[0] = -1;
}
void
lcp_lowerup(lcp *_this)
{
fsm_lowerup(&_this->fsm);
fsm_open(&_this->fsm);
if (_this->dialin_proxy != 0 &&
_this->dialin_proxy_lcp_renegotiation == 0) {
_this->fsm.state = OPENED;
lcp_open(&_this->fsm);
}
}
void
lcp_send_protrej(lcp *_this, u_char *pktp, int lpktp)
{
LCP_ASSERT(_this != NULL);
LCP_ASSERT(pktp != NULL);
fsm_sdata(&_this->fsm, PROTREJ, _this->fsm.id++, pktp, lpktp);
}
static const char *
lcp_auth_string(int auth)
{
switch (auth) {
case PPP_AUTH_PAP: return "PAP";
case PPP_AUTH_CHAP_MD5: return "MD5-CHAP";
case PPP_AUTH_CHAP_MS: return "MS-CHAP";
case PPP_AUTH_CHAP_MS_V2: return "MS-CHAP-V2";
case PPP_AUTH_EAP: return "EAP";
case 0: return "none";
default: return "ERROR";
}
}
static void
lcp_open(fsm *f)
{
lcp *_this;
int peer_auth = 0;
LCP_ASSERT(f != NULL);
_this = &f->ppp->lcp;
if (psm_opt_is_accepted(_this, pap))
peer_auth = PPP_AUTH_PAP;
else if (psm_opt_is_accepted(_this, chap))
peer_auth = PPP_AUTH_CHAP_MD5;
else if (psm_opt_is_accepted(_this, chapms))
peer_auth = PPP_AUTH_CHAP_MS;
else if (psm_opt_is_accepted(_this, chapms_v2))
peer_auth = PPP_AUTH_CHAP_MS_V2;
else if (psm_opt_is_accepted(_this, eap))
peer_auth = PPP_AUTH_EAP;
else {
if (_this->auth_order[0] > 0) {
fsm_log(f, LOG_INFO,
"failed to negotiate a auth protocol.");
fsm_close(f, "Authentication is required");
ppp_set_disconnect_cause(f->ppp,
PPP_DISCON_AUTH_PROTOCOL_UNACCEPTABLE,
_this->auth_order[0] ,
1 , NULL);
ppp_stop(f->ppp, "Authentication is required");
return;
}
}
f->ppp->peer_auth = peer_auth;
if (_this->xxxmru > 0 && f->ppp->peer_mru <= 0)
f->ppp->peer_mru = _this->xxxmru;
if (f->ppp->peer_mru <= 0)
f->ppp->peer_mru = f->ppp->mru;
LCP_ASSERT(f->ppp->peer_mru > 500);
fsm_log(f, LOG_INFO, "logtype=Opened mru=%d/%d auth=%s magic=%08x/%08x"
, f->ppp->mru, f->ppp->peer_mru
, lcp_auth_string(peer_auth)
, f->ppp->lcp.magic_number, f->ppp->lcp.peer_magic_number
);
lcp_reset_timeout(_this);
ppp_lcp_up(f->ppp);
}
static void
lcp_down(fsm *f)
{
lcp *_this;
if (f->ppp->disconnect_code == PPP_DISCON_NO_INFORMATION) {
ppp_set_disconnect_cause(f->ppp, PPP_DISCON_NORMAL, 0,
1 , NULL);
#ifdef USE_NPPPD_RADIUS
ppp_set_radius_terminate_cause(f->ppp,
RADIUS_TERMNATE_CAUSE_USER_REQUEST);
#endif
}
_this = &f->ppp->lcp;
UNTIMEOUT(lcp_timeout, _this);
}
static void
lcp_finished(fsm *f)
{
ppp_lcp_finished(f->ppp);
}
static void
lcp_resetci(fsm *f)
{
LCP_ASSERT(f != NULL);
if (!(f->ppp->lcp.dialin_proxy != 0 &&
f->ppp->lcp.dialin_proxy_lcp_renegotiation == 0)) {
memset(&f->ppp->lcp.opt, 0, sizeof(f->ppp->lcp.opt));
f->ppp->lcp.auth_order[0] = -1;
}
}
static int
lcp_cilen(fsm *f)
{
LCP_ASSERT(f != NULL);
return f->ppp->mru;
}
static int
lcp_add_auth(fsm *f, u_char **ucpp)
{
int i;
u_char *ucp;
lcp *_this;
ucp = *ucpp;
_this = &f->ppp->lcp;
for (i = 0; _this->auth_order[i] > 0 &&
i < countof(_this->auth_order); i++) {
switch (_this->auth_order[i]) {
case PPP_AUTH_PAP:
if (psm_opt_is_rejected(_this, pap))
break;
PUTCHAR(PPP_LCP_AUTH_PROTOCOL, ucp);
PUTCHAR(4, ucp);
PUTSHORT(PPP_AUTH_PAP, ucp);
psm_opt_set_requested(_this, pap, 1);
_this->lastauth = PPP_AUTH_PAP;
goto end_loop;
case PPP_AUTH_CHAP_MD5:
if (psm_opt_is_rejected(_this, chap))
break;
PUTCHAR(PPP_LCP_AUTH_PROTOCOL, ucp);
PUTCHAR(5, ucp);
PUTSHORT(PPP_AUTH_CHAP, ucp);
PUTCHAR(PPP_AUTH_CHAP_MD5, ucp);
psm_opt_set_requested(_this, chap, 1);
_this->lastauth = PPP_AUTH_CHAP_MD5;
goto end_loop;
case PPP_AUTH_CHAP_MS:
if (psm_opt_is_rejected(_this, chapms))
break;
PUTCHAR(PPP_LCP_AUTH_PROTOCOL, ucp);
PUTCHAR(5, ucp);
PUTSHORT(PPP_AUTH_CHAP, ucp);
PUTCHAR(PPP_AUTH_CHAP_MS, ucp);
psm_opt_set_requested(_this, chapms, 1);
_this->lastauth = PPP_AUTH_CHAP_MS;
goto end_loop;
case PPP_AUTH_CHAP_MS_V2:
if (psm_opt_is_rejected(_this, chapms_v2))
break;
PUTCHAR(PPP_LCP_AUTH_PROTOCOL, ucp);
PUTCHAR(5, ucp);
PUTSHORT(PPP_AUTH_CHAP, ucp);
PUTCHAR(PPP_AUTH_CHAP_MS_V2, ucp);
psm_opt_set_requested(_this, chapms_v2,1);
_this->lastauth = PPP_AUTH_CHAP_MS_V2;
goto end_loop;
case PPP_AUTH_EAP:
if (psm_opt_is_rejected(_this, eap))
break;
PUTCHAR(PPP_LCP_AUTH_PROTOCOL, ucp);
PUTCHAR(4, ucp);
PUTSHORT(PPP_AUTH_EAP, ucp);
psm_opt_set_requested(_this, eap, 1);
_this->lastauth = PPP_AUTH_EAP;
goto end_loop;
}
}
_this->lastauth = -1;
return -1;
end_loop:
*ucpp = ucp;
return 0;
}
static void
lcp_addci(fsm *f, u_char *ucp, int *lenp)
{
lcp *_this;
u_char *start_ucp = ucp;
LCP_ASSERT(f != NULL);
_this = &f->ppp->lcp;
if (!psm_opt_is_rejected(_this, mru)) {
PUTCHAR(PPP_LCP_MRU, ucp);
PUTCHAR(4, ucp);
if (_this->xxxmru > 0) {
PUTSHORT(_this->xxxmru, ucp);
} else {
PUTSHORT(f->ppp->mru, ucp);
}
psm_opt_set_requested(_this, mru, 1);
}
if (f->ppp->has_acf == 1) {
if (!psm_opt_is_rejected(_this, pfc)) {
PUTCHAR(PPP_LCP_PFC, ucp);
PUTCHAR(2, ucp);
psm_opt_set_requested(_this, pfc, 1);
}
if (!psm_opt_is_rejected(_this, acfc)) {
PUTCHAR(PPP_LCP_ACFC, ucp);
PUTCHAR(2, ucp);
psm_opt_set_requested(_this, acfc, 1);
}
}
PUTCHAR(PPP_LCP_MAGICNUMBER, ucp);
PUTCHAR(6, ucp);
PUTLONG(_this->magic_number, ucp);
if (f->ppp->peer_auth != 0) {
_this->auth_order[0] = f->ppp->peer_auth;
_this->auth_order[1] = -1;
} else if (_this->auth_order[0] < 0) {
lcp_load_authconfig(f);
}
lcp_add_auth(f, &ucp);
*lenp = ucp - start_ucp;
}
static int
lcp_reqci(fsm *f, u_char *inp, int *lenp, int reject_if_disagree)
{
uint32_t magic;
int type, len, rcode, mru, lrej;
u_char *inp0, *rejbuf, *nakbuf, *nakbuf0;
lcp *_this;
_this = &f->ppp->lcp;
rejbuf = NULL;
rcode = -1;
inp0 = inp;
lrej = 0;
if ((rejbuf = malloc(*lenp)) == NULL)
return -1;
if ((nakbuf0 = malloc(*lenp)) == NULL) {
free(rejbuf);
return -1;
}
nakbuf = nakbuf0;
#define remlen() (*lenp - (inp - inp0))
#define LCP_OPT_PEER_ACCEPTED(opt) \
psm_peer_opt_set_accepted(&f->ppp->lcp, opt, 1);
f->ppp->lcp.recv_reqs++;
while (remlen() >= 2) {
GETCHAR(type, inp);
GETCHAR(len, inp);
if (len <= 0 || remlen() + 2 < len)
goto fail;
switch (type) {
case PPP_LCP_MRU:
if (len != 4)
goto fail;
GETSHORT(mru, inp);
f->ppp->peer_mru = mru;
if (mru < NPPPD_MIN_MRU) {
if (reject_if_disagree) {
inp -= 2;
goto reject;
}
if (lrej > 0) {
} else {
inp -= 2;
memcpy(nakbuf, inp, len);
nakbuf += len;
inp += 2;
PUTSHORT(f->ppp->mru, nakbuf);
rcode = CONFNAK;
}
} else
LCP_OPT_PEER_ACCEPTED(mru);
break;
case PPP_LCP_MAGICNUMBER:
if (len != 6)
goto fail;
GETLONG(magic, inp);
if (magic == _this->magic_number) {
inp -= 4;
goto reject;
}
_this->peer_magic_number = magic;
break;
case PPP_LCP_PFC:
if (len != 2)
goto fail;
LCP_OPT_PEER_ACCEPTED(pfc);
break;
case PPP_LCP_ACFC:
if (len != 2)
goto fail;
LCP_OPT_PEER_ACCEPTED(acfc);
break;
case PPP_LCP_AUTH_PROTOCOL:
case PPP_LCP_QUALITY_PROTOCOL:
default:
reject:
inp -= 2;
memcpy(rejbuf + lrej, inp, len);
lrej += len;
inp += len;
rcode = CONFREJ;
}
continue;
}
if (rcode == -1)
rcode = CONFACK;
fail:
switch (rcode) {
case CONFREJ:
memcpy(inp0, rejbuf, lrej);
*lenp = lrej;
break;
case CONFNAK:
memcpy(inp0, nakbuf0, nakbuf - nakbuf0);
*lenp = nakbuf - nakbuf0;
break;
}
if (rcode != CONFACK) {
psm_peer_opt_set_accepted(&f->ppp->lcp, mru, 0);
psm_peer_opt_set_accepted(&f->ppp->lcp, pfc, 0);
psm_peer_opt_set_accepted(&f->ppp->lcp, acfc, 0);
}
free(rejbuf);
free(nakbuf0);
return rcode;
#undef remlen
#undef LCP_OPT_PEER_ACCEPTED
}
static int
lcp_ackci(fsm *f, u_char *inp, int inlen)
{
int chapalg, authproto, type, len, mru, magic;
u_char *inp0;
#define remlen() (inlen - (inp - inp0))
#define LCP_OPT_ACCEPTED(opt) \
if (!psm_opt_is_requested(&f->ppp->lcp, opt)) \
goto fail; \
psm_opt_set_accepted(&f->ppp->lcp, opt, 1);
f->ppp->lcp.recv_ress++;
inp0 = inp;
while (remlen() >= 2) {
GETCHAR(type, inp);
GETCHAR(len, inp);
if (len <= 0 || remlen() + 2 < len)
goto fail;
switch (type) {
case PPP_LCP_MAGICNUMBER:
if (len != 6)
goto fail;
GETLONG(magic, inp);
if (f->ppp->lcp.magic_number != magic)
goto fail;
break;
case PPP_LCP_MRU:
if (len != 4)
goto fail;
LCP_OPT_ACCEPTED(mru);
GETSHORT(mru, inp);
break;
case PPP_LCP_AUTH_PROTOCOL:
if (len < 4)
goto fail;
GETSHORT(authproto, inp);
switch (authproto) {
case PPP_AUTH_PAP:
if (len != 4)
goto fail;
LCP_OPT_ACCEPTED(pap);
break;
case PPP_AUTH_CHAP:
if (len != 5)
goto fail;
GETCHAR(chapalg, inp);
switch (chapalg) {
case PPP_AUTH_CHAP_MD5:
LCP_OPT_ACCEPTED(chap);
break;
case PPP_AUTH_CHAP_MS:
LCP_OPT_ACCEPTED(chapms);
break;
case PPP_AUTH_CHAP_MS_V2:
LCP_OPT_ACCEPTED(chapms_v2);
break;
}
break;
case PPP_AUTH_EAP:
if (len != 4)
goto fail;
LCP_OPT_ACCEPTED(eap);
break;
}
break;
case PPP_LCP_PFC:
if (len != 2)
goto fail;
LCP_OPT_ACCEPTED(pfc);
break;
case PPP_LCP_ACFC:
if (len != 2)
goto fail;
LCP_OPT_ACCEPTED(acfc);
break;
default:
goto fail;
}
}
return 1;
fail:
fsm_log(f, LOG_ERR, "Received unexpected ConfAck.");
if (debug_get_debugfp() != NULL)
show_hd(debug_get_debugfp(), inp, remlen());
return 0;
#undef LCP_OPT_ACCEPTED
}
static int
lcp_nakci(fsm *f, u_char *inp, int inlen)
{
int chapalg, authproto, type, len, mru;
u_char *inp0;
lcp *_this;
const char *peer_auth = "unknown";
#define remlen() (inlen - (inp - inp0))
#define LCP_OPT_REJECTED(opt) \
if (!psm_opt_is_requested(&f->ppp->lcp, opt)) \
goto fail; \
psm_opt_set_rejected(&f->ppp->lcp, opt, 1);
f->ppp->lcp.recv_ress++;
inp0 = inp;
_this = &f->ppp->lcp;
while (remlen() >= 2) {
GETCHAR(type, inp);
GETCHAR(len, inp);
if (len <= 0 || remlen() + 2 < len)
goto fail;
switch (type) {
case PPP_LCP_MRU:
if (len < 4)
goto fail;
GETSHORT(mru, inp);
fsm_log(f, LOG_NOTICE,
"ignored ConfNak from the peer: mru=%d", mru);
_this->xxxmru = mru;
break;
case PPP_LCP_AUTH_PROTOCOL:
if (len < 4)
goto fail;
switch (_this->lastauth) {
case PPP_AUTH_PAP:
psm_opt_set_rejected(_this, pap, 1);
break;
case PPP_AUTH_CHAP_MD5:
psm_opt_set_rejected(_this, chap, 1);
break;
case PPP_AUTH_CHAP_MS:
psm_opt_set_rejected(_this, chapms, 1);
break;
case PPP_AUTH_CHAP_MS_V2:
psm_opt_set_rejected(_this, chapms_v2, 1);
break;
case PPP_AUTH_EAP:
psm_opt_set_rejected(_this, eap, 1);
break;
}
GETSHORT(authproto, inp);
switch (authproto) {
case PPP_AUTH_PAP:
if (psm_opt_is_requested(_this, pap))
psm_opt_set_accepted(_this, pap, 1);
peer_auth = "pap";
break;
case PPP_AUTH_CHAP:
chapalg = 0;
if (len == 5)
GETCHAR(chapalg, inp);
switch (chapalg) {
case PPP_AUTH_CHAP_MD5:
if (psm_opt_is_requested(_this, chap))
psm_opt_set_accepted(_this,
chap, 1);
peer_auth = "chap";
break;
case PPP_AUTH_CHAP_MS:
if (psm_opt_is_requested(_this, chapms))
psm_opt_set_accepted(_this,
chapms, 1);
peer_auth = "mschap";
break;
case PPP_AUTH_CHAP_MS_V2:
if (psm_opt_is_requested(_this,
chapms_v2))
psm_opt_set_accepted(_this,
chapms_v2, 1);
peer_auth = "mschap_v2";
break;
default:
fsm_log(f, LOG_INFO,
"Nacked chap algorithm is "
"unknown(%d).", chapalg);
peer_auth = "unknown";
break;
}
break;
case PPP_AUTH_EAP:
if (len != 4)
goto fail;
peer_auth = "eap";
if (psm_opt_is_requested(_this, eap))
psm_opt_set_accepted(_this, eap, 1);
break;
}
if (NO_AUTH_AGREEABLE(_this)) {
fsm_log(f, LOG_INFO, "No authentication "
"protocols are agreeable. peer's "
"auth proto=%s",
peer_auth);
ppp_set_disconnect_cause(f->ppp,
PPP_DISCON_AUTH_PROTOCOL_UNACCEPTABLE,
authproto, 2 ,
NULL);
ppp_stop(f->ppp, "Authentication is required");
return 1;
}
break;
case PPP_LCP_PFC:
if (len != 2)
goto fail;
LCP_OPT_REJECTED(pfc);
break;
case PPP_LCP_ACFC:
if (len != 2)
goto fail;
LCP_OPT_REJECTED(acfc);
break;
default:
goto fail;
}
}
return 1;
fail:
log_printf(LOG_ERR, "Received unexpected ConfNak.");
if (debug_get_debugfp() != NULL)
show_hd(debug_get_debugfp(), inp, inlen);
return 0;
#undef remlen
#undef LCP_OPT_REJECTED
}
static int
lcp_rejci(fsm *f, u_char *inp, int inlen)
{
int chapalg, authproto, type, len, mru;
u_char *inp0;
lcp *_this;
#define remlen() (inlen - (inp - inp0))
#define LCP_OPT_REJECTED(opt) \
if (!psm_opt_is_requested(&f->ppp->lcp, opt)) \
goto fail; \
psm_opt_set_rejected(&f->ppp->lcp, opt, 1);
f->ppp->lcp.recv_ress++;
inp0 = inp;
_this = &f->ppp->lcp;
while (remlen() >= 2) {
GETCHAR(type, inp);
GETCHAR(len, inp);
if (len <= 0 || remlen() + 2 < len)
goto fail;
switch (type) {
case PPP_LCP_MAGICNUMBER:
if (f->ppp->lcp.echo_interval > 0)
goto fail;
inp += 4;
break;
case PPP_LCP_MRU:
LCP_OPT_REJECTED(mru);
GETSHORT(mru, inp);
break;
case PPP_LCP_AUTH_PROTOCOL:
if (len < 4)
goto fail;
GETSHORT(authproto, inp);
switch (authproto) {
case PPP_AUTH_PAP:
if (len != 4)
goto fail;
LCP_OPT_REJECTED(pap);
break;
case PPP_AUTH_CHAP:
chapalg = 0;
if (len == 5)
GETCHAR(chapalg, inp);
switch (chapalg) {
case PPP_AUTH_CHAP_MD5:
LCP_OPT_REJECTED(chap);
break;
case PPP_AUTH_CHAP_MS:
LCP_OPT_REJECTED(chapms);
break;
case PPP_AUTH_CHAP_MS_V2:
LCP_OPT_REJECTED(chapms_v2);
break;
default:
fsm_log(f, LOG_INFO,
"Rejected chap algorithm is "
"unknown(%d).", chapalg);
break;
}
break;
case PPP_AUTH_EAP:
if (len != 4)
goto fail;
LCP_OPT_REJECTED(eap);
break;
}
if (NO_AUTH_AGREEABLE(_this)) {
fsm_log(f, LOG_INFO, "No authentication "
"protocols are agreeable.");
ppp_set_disconnect_cause(f->ppp,
PPP_DISCON_AUTH_PROTOCOL_UNACCEPTABLE,
authproto, 1 , NULL);
ppp_stop(f->ppp, "Authentication is required");
return 1;
}
break;
case PPP_LCP_PFC:
if (len != 2)
goto fail;
LCP_OPT_REJECTED(pfc);
break;
case PPP_LCP_ACFC:
if (len != 2)
goto fail;
LCP_OPT_REJECTED(acfc);
break;
default:
goto fail;
}
}
return 1;
fail:
log_printf(LOG_ERR, "Received unexpected ConfRej.");
if (debug_get_debugfp() != NULL)
show_hd(debug_get_debugfp(), inp, inlen);
return 0;
#undef remlen
}
static void
lcp_rcoderej(fsm *f, u_char *inp, int inlen)
{
uint16_t proto;
fsm *rejfsm;
if (inlen < 2) {
fsm_log(f, LOG_WARNING, "Received short ProtRej packet.");
return;
}
GETSHORT(proto, inp);
rejfsm = NULL;
switch (proto) {
case PPP_PROTO_LCP:
rejfsm = &f->ppp->lcp.fsm;
break;
case PPP_PROTO_PAP:
fsm_log(f, LOG_WARNING, "our PAP packet is rejected");
return;
case PPP_PROTO_CHAP:
fsm_log(f, LOG_WARNING, "our CHAP packet is rejected");
return;
case PPP_PROTO_EAP:
fsm_log(f, LOG_ERR, "our EAP packet is rejected");
ppp_stop(f->ppp, "Authentication Required");
break;
case PPP_PROTO_NCP | NCP_IPCP:
rejfsm = &f->ppp->ipcp.fsm;
break;
case PPP_PROTO_NCP | NCP_CCP:
rejfsm = &f->ppp->ccp.fsm;
break;
}
if (rejfsm == NULL) {
fsm_log(f, LOG_WARNING,
"Received ProtRej packet for unknown protocol=(%d/%04x)",
proto, proto);
return;
}
fsm_protreject(rejfsm);
return;
}
static void
lcp_reset_timeout(void *ctx)
{
lcp *_this;
_this = ctx;
if (_this->echo_interval > 0) {
if (_this->echo_failures == 0) {
TIMEOUT(lcp_timeout, _this, _this->echo_interval);
} else {
TIMEOUT(lcp_timeout, _this, _this->echo_retry_interval);
}
} else {
UNTIMEOUT(lcp_timeout, _this);
}
}
static void
lcp_timeout(void *ctx)
{
lcp *_this;
u_char *cp, buf[32];
_this = ctx;
if (_this->echo_failures >= _this->echo_max_retries) {
fsm_log(&_this->fsm, LOG_NOTICE, "keepalive failure.");
if (_this->fsm.ppp != NULL) {
#ifdef USE_NPPPD_RADIUS
ppp_set_radius_terminate_cause(_this->fsm.ppp,
RADIUS_TERMNATE_CAUSE_IDLE_TIMEOUT);
#endif
ppp_stop(_this->fsm.ppp, NULL);
}
return;
}
cp = buf;
PUTLONG(_this->magic_number, cp);
fsm_sdata(&_this->fsm, ECHOREQ, _this->fsm.id++, buf, 4);
_this->echo_failures++;
lcp_reset_timeout(_this);
}
static int
lcp_rechoreq(fsm *f, int id, u_char *inp, int inlen)
{
u_char *inp0;
lcp *_this;
int len;
if (inlen < 4)
return 0;
_this = &f->ppp->lcp;
inp0 = inp;
PUTLONG(_this->magic_number, inp)
len = MINIMUM(inlen, f->ppp->peer_mru - 8);
fsm_sdata(f, ECHOREP, id, inp0, len);
return 1;
}
static int
lcp_ext(fsm *f, int code, int id, u_char *inp, int inlen)
{
lcp *_this;
uint32_t magic;
char buf[256];
int i, len;
_this = &f->ppp->lcp;
switch (code) {
case IDENTIFICATION:
if (inlen > 4) {
GETLONG(magic, inp);
inlen -= 4;
memset(buf, 0, sizeof(buf));
len = MINIMUM(inlen, sizeof(buf) - 1);
memcpy(buf, inp, len);
buf[len] = '\0';
for (i = 0; i < len; i++) {
if (!isprint((unsigned char)buf[i]))
buf[i] = '.';
}
fsm_log(f, LOG_INFO,
"RecvId magic=%08x text=%s", magic, buf);
}
return 1;
case PROTREJ:
lcp_rcoderej(f, inp, inlen);
return 1;
case ECHOREP:
if (f->state == OPENED) {
if (inlen >= 4) {
GETLONG(magic, inp);
if (_this->peer_magic_number == magic) {
_this->echo_failures = 0;
lcp_reset_timeout(_this);
}
}
}
return 1;
case ECHOREQ:
if (f->state == OPENED)
return lcp_rechoreq(f, id, inp, inlen);
return 1;
}
return 0;
}
static void
lcp_load_authconfig(fsm *f)
{
int i;
lcp *_this;
struct tunnconf *conf;
i = 0;
_this = &f->ppp->lcp;
conf = ppp_get_tunnconf(f->ppp);
if ((conf->auth_methods & NPPPD_AUTH_METHODS_MSCHAPV2) != 0) {
_this->auth_order[i++] = PPP_AUTH_CHAP_MS_V2;
psm_opt_set_enabled(_this,chapms_v2, 1);
}
if ((conf->auth_methods & NPPPD_AUTH_METHODS_CHAP) != 0) {
_this->auth_order[i++] = PPP_AUTH_CHAP_MD5;
psm_opt_set_enabled(_this, chap, 1);
}
if ((conf->auth_methods & NPPPD_AUTH_METHODS_PAP) != 0) {
_this->auth_order[i++] = PPP_AUTH_PAP;
psm_opt_set_enabled(_this, pap, 1);
}
_this->auth_order[i] = -1;
}
int
lcp_dialin_proxy(lcp *_this, dialin_proxy_info *dpi, int renegotiation,
int force_renegotiation)
{
int i, authok;
_this->dialin_proxy = 1;
lcp_load_authconfig(&_this->fsm);
authok = 0;
if (dpi->auth_type != 0) {
for (i = 0; _this->auth_order[i] > 0; i++) {
if (_this->auth_order[i] != dpi->auth_type)
continue;
authok = 1;
break;
}
}
if (!authok) {
if (!renegotiation) {
fsm_log(&_this->fsm, LOG_NOTICE,
"dialin-proxy failed. auth-method=%s is "
"not enabled. Try 'l2tp.dialin.lcp_renegotion'",
lcp_auth_string(dpi->auth_type));
return 1;
}
_this->dialin_proxy_lcp_renegotiation = 1;
}
if (force_renegotiation)
_this->dialin_proxy_lcp_renegotiation = 1;
if (_this->dialin_proxy_lcp_renegotiation == 0) {
_this->fsm.ppp->peer_auth = dpi->auth_type;
psm_opt_set_rejected(_this, mru, 1);
psm_opt_set_rejected(_this, pfc, 1);
psm_opt_set_rejected(_this, acfc, 1);
psm_opt_set_rejected(_this, pap, 1);
psm_opt_set_rejected(_this, chap, 1);
psm_opt_set_rejected(_this, chapms, 1);
psm_opt_set_rejected(_this, chapms_v2, 1);
psm_opt_set_rejected(_this, eap, 1);
}
switch (dpi->auth_type) {
case PPP_AUTH_PAP:
pap_proxy_authen_prepare(&_this->fsm.ppp->pap, dpi);
break;
case PPP_AUTH_CHAP_MD5:
chap_proxy_authen_prepare(&_this->fsm.ppp->chap, dpi);
break;
}
if (lcp_proxy_sent_ci(&_this->fsm, dpi->last_sent_lcp.data,
dpi->last_sent_lcp.ldata) != 0) {
fsm_log(&_this->fsm, LOG_NOTICE,
"dialin-proxy failed. couldn't use proxied lcp.");
return 1;
}
if (lcp_proxy_recv_ci(&_this->fsm, dpi->last_recv_lcp.data,
dpi->last_recv_lcp.ldata) != 0) {
fsm_log(&_this->fsm, LOG_NOTICE,
"dialin-proxy failed. couldn't use proxied lcp.");
return 1;
}
fsm_log(&_this->fsm, LOG_INFO,
"dialin-proxy user=%s auth-type=%s renegotiate=%s",
dpi->username,
(dpi->auth_type == 0)? "none" : lcp_auth_string(dpi->auth_type),
(_this->dialin_proxy_lcp_renegotiation != 0)? "yes" : "no");
if (_this->dialin_proxy_lcp_renegotiation == 0)
_this->fsm.flags |= OPT_SILENT;
else
_this->fsm.flags &= ~OPT_SILENT;
return 0;
}
static int
lcp_proxy_recv_ci(fsm *f, u_char *inp, int inlen)
{
int type, mru, len;
uint32_t magic;
u_char *inp0;
lcp *_this;
#define remlen() (inlen - (inp - inp0))
#define LCP_OPT_PEER_ACCEPTED(opt) \
psm_peer_opt_set_rejected(&f->ppp->lcp, opt, 0); \
psm_peer_opt_set_requested(&f->ppp->lcp, opt, 1); \
psm_peer_opt_set_accepted(&f->ppp->lcp, opt, 1);
_this = &f->ppp->lcp;
inp0 = inp;
while (remlen() >= 2) {
GETCHAR(type, inp);
GETCHAR(len, inp);
if (len <= 0 || remlen() + 2 < len)
goto fail;
switch (type) {
case PPP_LCP_MRU:
if (len != 4)
goto fail;
GETSHORT(mru, inp);
f->ppp->peer_mru = mru;
if (mru < NPPPD_MIN_MRU)
goto fail;
else
LCP_OPT_PEER_ACCEPTED(mru);
break;
case PPP_LCP_MAGICNUMBER:
if (len != 6)
goto fail;
GETLONG(magic, inp);
if (magic == _this->magic_number)
goto fail;
_this->peer_magic_number = magic;
break;
case PPP_LCP_PFC:
if (len != 2)
goto fail;
LCP_OPT_PEER_ACCEPTED(pfc);
break;
case PPP_LCP_ACFC:
if (len != 2)
goto fail;
LCP_OPT_PEER_ACCEPTED(acfc);
break;
case PPP_LCP_ACCM:
if (len != 6)
goto fail;
inp += (len - 2);
break;
default:
goto fail;
}
}
#undef remlen
#undef LCP_OPT_PEER_ACCEPTED
return 0;
fail:
return 1;
}
static int
lcp_proxy_sent_ci(fsm *f, u_char *inp, int inlen)
{
int chapalg, authproto, type, len, mru, magic;
u_char *inp0;
#define remlen() (inlen - (inp - inp0))
#define LCP_OPT_ACCEPTED(opt) \
if (f->ppp->lcp.dialin_proxy_lcp_renegotiation == 0) { \
psm_opt_set_rejected(&f->ppp->lcp, opt, 0); \
psm_opt_set_requested(&f->ppp->lcp, opt, 1); \
psm_opt_set_accepted(&f->ppp->lcp, opt, 1); \
}
inp0 = inp;
while (remlen() >= 2) {
GETCHAR(type, inp);
GETCHAR(len, inp);
if (len <= 0 || remlen() + 2 < len)
goto fail;
switch (type) {
case PPP_LCP_MAGICNUMBER:
if (len != 6)
goto fail;
GETLONG(magic, inp);
f->ppp->lcp.magic_number = magic;
break;
case PPP_LCP_MRU:
if (len != 4)
goto fail;
LCP_OPT_ACCEPTED(mru);
GETSHORT(mru, inp);
f->ppp->lcp.xxxmru = mru;
break;
case PPP_LCP_AUTH_PROTOCOL:
if (len < 4)
goto fail;
GETSHORT(authproto, inp);
switch (authproto) {
case PPP_AUTH_PAP:
if (len != 4)
goto fail;
LCP_OPT_ACCEPTED(pap);
break;
case PPP_AUTH_CHAP:
if (len != 5)
goto fail;
GETCHAR(chapalg, inp);
switch (chapalg) {
case PPP_AUTH_CHAP_MD5:
LCP_OPT_ACCEPTED(chap);
break;
case PPP_AUTH_CHAP_MS:
LCP_OPT_ACCEPTED(chapms);
break;
case PPP_AUTH_CHAP_MS_V2:
LCP_OPT_ACCEPTED(chapms_v2);
break;
}
break;
case PPP_AUTH_EAP:
if (len != 4)
goto fail;
LCP_OPT_ACCEPTED(eap);
break;
}
break;
case PPP_LCP_PFC:
if (len != 2)
goto fail;
LCP_OPT_ACCEPTED(pfc);
break;
case PPP_LCP_ACFC:
if (len != 2)
goto fail;
LCP_OPT_ACCEPTED(acfc);
break;
case PPP_LCP_ACCM:
if (len != 6)
goto fail;
inp += (len - 2);
break;
default:
goto fail;
}
}
return 0;
fail:
return 1;
#undef LCP_OPT_ACCEPTED
}