1075 lines
25 KiB
C
1075 lines
25 KiB
C
/* PPP authorization protocols
|
|
*/
|
|
|
|
#ident "$Revision: 1.30 $"
|
|
|
|
#include <syslog.h>
|
|
#include <pwd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/fcntl.h>
|
|
#include <arpa/inet.h>
|
|
#include <crypt.h>
|
|
|
|
#include "ppp.h"
|
|
|
|
|
|
struct pap_pkt {
|
|
u_char code;
|
|
u_char id;
|
|
u_short len;
|
|
u_char slen;
|
|
char d[1];
|
|
};
|
|
|
|
struct chap_pkt {
|
|
u_char code;
|
|
u_char id;
|
|
u_short len;
|
|
union {
|
|
struct {
|
|
u_char vsize;
|
|
struct cval v; /* what we use */
|
|
char name[1];
|
|
} chal;
|
|
struct {
|
|
u_char vsize;
|
|
# define CHAP_RESP_MD5_LEN MD5_DIGEST_LEN
|
|
u_char val[CHAP_RESP_MD5_LEN];
|
|
char name[1];
|
|
} resp;
|
|
char msg[1];
|
|
} u;
|
|
};
|
|
|
|
|
|
#define A ppp->auth
|
|
#define ANAME A.name
|
|
#define IBUF_PAP ((struct pap_pkt*)&(ibuf.un.info[0]))
|
|
#define OBUF_PAP ((struct pap_pkt*)&(obuf.un.info[0]))
|
|
#define IBUF_CHAP ((struct chap_pkt*)&(ibuf.un.info[0]))
|
|
#define OBUF_CHAP ((struct chap_pkt*)&(obuf.un.info[0]))
|
|
|
|
/* PAP packet codes */
|
|
#define PAP_CODE_REQ 1 /* Authenticate-Request */
|
|
#define PAP_CODE_ACK 2 /* Authenticate-Ack */
|
|
#define PAP_CODE_NAK 3 /* Authenticate-Nak */
|
|
|
|
/* CHAP packet codes */
|
|
#define CHAP_CODE_CHALLENGE 1
|
|
#define CHAP_CODE_RESPONSE 2
|
|
#define CHAP_CODE_SUCCESS 3
|
|
#define CHAP_CODE_FAILURE 4
|
|
|
|
|
|
/* check username and (if PAP) password against /etc/passwd
|
|
*/
|
|
static int /* 1=ok */
|
|
ck_name(struct ppp *ppp,
|
|
u_short proto,
|
|
char *name,
|
|
char *passwd)
|
|
{
|
|
if (strlen(name) >= sizeof(A.recvd_name)) {
|
|
log_complain(ANAME, "received name \"%s\" too long",
|
|
name);
|
|
return 0;
|
|
}
|
|
|
|
/* Do not let the peer try another name, to prevent a bad
|
|
* peer from probing or using us as an oracle.
|
|
*/
|
|
if (A.recvd_name[0] != '\0') {
|
|
if (strcmp(name, A.recvd_name)) {
|
|
log_complain(ANAME, "received wrong (changed) name"
|
|
" \"%s\" instead of \"%s\"",
|
|
name, A.recvd_name);
|
|
return 0;
|
|
}
|
|
} else {
|
|
(void)strcpy(A.recvd_name, name);
|
|
|
|
/* If no names configured, than accept any name
|
|
* If any names were specified in the control file, then
|
|
* require that the peer give us one of the names on the
|
|
* specified list.
|
|
*/
|
|
if (A.recv_names.nm[0] != '\0') {
|
|
struct recv_name *np = &A.recv_names;
|
|
while (strcmp(np->nm, name)) {
|
|
np = np->nxt;
|
|
if (!np) {
|
|
log_complain(ANAME,
|
|
"received wrong name"
|
|
" \"%s\"",
|
|
name);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (proto == PPP_PAP) {
|
|
char *cp;
|
|
struct passwd *pw = getpwnam(name);
|
|
|
|
if (!pw) {
|
|
log_complain(ANAME, "no such username as"
|
|
" received PAP name \"%s\"",
|
|
name);
|
|
return 0;
|
|
}
|
|
|
|
cp = pw->pw_passwd;
|
|
if (cp != 0 && *cp != '\0'
|
|
&& strcmp(crypt(passwd, cp), cp)) {
|
|
log_complain(ANAME,
|
|
"received bad PAP password for \"%s\"",
|
|
name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* send PAP ACK or NAK
|
|
*/
|
|
static void
|
|
pap_acknak(struct ppp *ppp,
|
|
u_char code,
|
|
u_char id,
|
|
char *msg,
|
|
char *debug_msg)
|
|
{
|
|
if (code == PAP_CODE_ACK) {
|
|
log_debug(2,ANAME, "send PAP Ack with \"%s\" %s",
|
|
msg, debug_msg);
|
|
} else {
|
|
log_complain(ANAME, "send PAP Nak with \"%s\" %s",
|
|
msg, debug_msg);
|
|
}
|
|
|
|
OBUF_PAP->code = code;
|
|
OBUF_PAP->id = id;
|
|
OBUF_PAP->slen = strlen(msg);
|
|
(void)strcpy(OBUF_PAP->d, msg);
|
|
obuf.bits = 0;
|
|
obuf.proto = PPP_PAP;
|
|
OBUF_PAP->len = OBUF_PAP->slen + sizeof(struct pap_pkt)-1;
|
|
ppp_send(ppp,&obuf,OBUF_PAP->len);
|
|
}
|
|
|
|
|
|
/* send CHAP Success or Failure
|
|
*/
|
|
static void
|
|
chap_acknak(struct ppp *ppp,
|
|
u_char code,
|
|
u_char id)
|
|
{
|
|
char *peer_msg;
|
|
|
|
|
|
if (code == CHAP_CODE_SUCCESS) {
|
|
peer_msg = "howdy";
|
|
log_debug(2,ANAME, "send CHAP Success with \"%s\" %s",
|
|
peer_msg, "");
|
|
} else {
|
|
peer_msg = "wrong";
|
|
log_complain(ANAME,"send CHAP Failure with \"%s\" %s",
|
|
peer_msg, "");
|
|
}
|
|
|
|
OBUF_CHAP->code = code;
|
|
OBUF_CHAP->id = id;
|
|
(void)strcpy(OBUF_CHAP->u.msg, peer_msg);
|
|
obuf.bits = 0;
|
|
obuf.proto = PPP_CHAP;
|
|
OBUF_CHAP->len = strlen(peer_msg) + 4;
|
|
ppp_send(ppp,&obuf,OBUF_CHAP->len);
|
|
|
|
if (code != CHAP_CODE_SUCCESS) {
|
|
/* no second chance */
|
|
set_tr_msg(&ppp->lcp.fsm, "authentication failure");
|
|
lcp_event(ppp,FSM_CLOSE);
|
|
}
|
|
}
|
|
|
|
|
|
/* Use the right control file entry if we were using a generic model.
|
|
* Use the name the peer gave us as the tag for the control file entry.
|
|
* The generic control file entry must have either include no names
|
|
* (and so any name is ok) or one of the specified names must have been
|
|
* what the peer sent us.
|
|
*/
|
|
static int /* 1=need to renegotiate */
|
|
reparse(struct ppp *ppp)
|
|
{
|
|
int result = 0;
|
|
|
|
if (reconfigure) {
|
|
if (remote)
|
|
free(remote);
|
|
remote = strdup(A.recvd_name);
|
|
log_debug(1,ANAME,"re-parse control file using label \"%s\"",
|
|
remote);
|
|
|
|
result = parse_conf(1);
|
|
|
|
/* ensure new MP parameters are used */
|
|
(void)set_mp(ppp);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Try to join a resident daemon
|
|
* If we do rendezvous, this function does not return, but will
|
|
* indirectly exit().
|
|
*/
|
|
static void
|
|
try_rend(struct ppp *ppp)
|
|
{
|
|
/* add remote host name to rendezvous node */
|
|
if (remhost.sin_addr.s_addr != 0
|
|
&& !def_remhost)
|
|
(void)add_rend_name("",remhost_str);
|
|
|
|
/* if the resident daemon is alive, join it */
|
|
if (!reconfigure
|
|
&& make_rend(ppp->rem_epdis[0] != '\0'))
|
|
do_rend(ppp);
|
|
}
|
|
|
|
|
|
/* renegotiate LCP parameters after reconfiguring
|
|
*/
|
|
static void
|
|
renegotiate(struct ppp *ppp)
|
|
{
|
|
log_debug(2,ANAME,"renegotiate LCP parameters");
|
|
|
|
lcp_event(ppp, FSM_DOWN);
|
|
lcp_event(ppp, FSM_UP);
|
|
|
|
try_rend(ppp);
|
|
}
|
|
|
|
|
|
void
|
|
auth_init(struct ppp *ppp)
|
|
{
|
|
A.chap_id = (u_char)newmagic();
|
|
A.pap_id = A.chap_id;
|
|
A.recvd_name[0] = '\0';
|
|
}
|
|
|
|
|
|
/* get ready to authenticate, or just skip it
|
|
*/
|
|
void
|
|
auth_start(struct ppp *ppp)
|
|
{
|
|
char *recv, *send;
|
|
|
|
|
|
++A.pap_id;
|
|
|
|
if ((A.we_happy = (A.our_proto == 0)))
|
|
A.we_happy_time = clk.tv_sec;
|
|
A.peer_happy = (A.peer_proto == 0);
|
|
|
|
if (!A.we_happy || !A.peer_happy) {
|
|
switch (A.our_proto) {
|
|
case PPP_PAP:
|
|
recv = "PAP requests";
|
|
break;
|
|
case PPP_CHAP:
|
|
recv = "CHAP responses";
|
|
break;
|
|
default:
|
|
recv = "no authentication";
|
|
break;
|
|
}
|
|
switch (A.peer_proto) {
|
|
case PPP_PAP:
|
|
send = "PAP requests";
|
|
break;
|
|
case PPP_CHAP:
|
|
send = "CHAP responses";
|
|
break;
|
|
default:
|
|
send = "no authentication";
|
|
break;
|
|
}
|
|
log_debug(2,ANAME,
|
|
"will send %s %s receive %s",
|
|
send,
|
|
A.our_proto == A.peer_proto ? "and" : "but",
|
|
recv);
|
|
}
|
|
|
|
A.stime = clk.tv_sec;
|
|
auth_time(ppp); /* send 1st request */
|
|
|
|
auth_done(ppp);
|
|
}
|
|
|
|
|
|
/* Try to advance to the network phase if authentication went well.
|
|
*/
|
|
void
|
|
auth_done(struct ppp *ppp)
|
|
{
|
|
if (ppp->phase != AUTH_PHASE)
|
|
return;
|
|
|
|
/* We might now know the name or the address of the peer
|
|
* and be able to rendezvous.
|
|
*/
|
|
try_rend(ppp);
|
|
|
|
/* go to the next phase only if not waiting for a password */
|
|
if (A.we_happy && A.peer_happy) {
|
|
log_phase(ppp,NET_PHASE);
|
|
|
|
if (link_mux(ppp) < 0) {
|
|
set_tr_msg(&ppp->lcp.fsm,"MP system failure");
|
|
lcp_event(ppp,FSM_CLOSE);
|
|
return;
|
|
}
|
|
|
|
/* Start IP if it was not already started for another
|
|
* link in an MP bundle.
|
|
* We should not start CCP, because the peer might not
|
|
* have been able to start CCP, because it does not know
|
|
* what IP addresses to use yet, and so has not been
|
|
* able to build its STREAMS mux. If we start CCP now,
|
|
* it might have to discard our Configure-Request,
|
|
* causing us to time out.
|
|
*/
|
|
if (!mp_on || numdevs <= 1)
|
|
ipcp_go(ppp);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
pap_ipkt(struct ppp *ppp)
|
|
{
|
|
int ulen, plen;
|
|
char *name, *passwd;
|
|
|
|
|
|
if (ibuf_info_len < (int)IBUF_PAP->len) {
|
|
log_complain(ANAME,"discard PAP packet with %d bytes "
|
|
"but claiming %d",
|
|
ibuf_info_len, IBUF_PAP->len);
|
|
return;
|
|
}
|
|
|
|
if (ppp->phase != AUTH_PHASE
|
|
&& ppp->phase != NET_PHASE) {
|
|
log_debug(2,ANAME,"discard PAP packet received in %s phase",
|
|
phase_name(ppp->phase));
|
|
return;
|
|
}
|
|
|
|
switch (IBUF_PAP->code) {
|
|
case PAP_CODE_REQ: /* Authenticate-Request */
|
|
if (ibuf.bits != 0) { /* MP encapsulation is forbidden */
|
|
log_complain(ANAME,"discard MP PAP Request ID=%#x",
|
|
IBUF_PAP->id);
|
|
break;
|
|
}
|
|
|
|
log_debug(2,ANAME,"receive PAP request ID=%#x", IBUF_PAP->id);
|
|
ulen = IBUF_PAP->slen;
|
|
name = &IBUF_PAP->d[0];
|
|
if (ulen > (int)IBUF_PAP->len-4-2) {
|
|
log_complain(ANAME,
|
|
"received bad PAP name length of %d",
|
|
ulen);
|
|
break;
|
|
}
|
|
if (ulen > MAX_PAP_NAME_LEN) {
|
|
log_complain(ANAME,"Warning: PAP names must be"
|
|
" UNIX usernames,"
|
|
" %d extra bytes will be ignored",
|
|
ulen - MAX_PAP_NAME_LEN);
|
|
}
|
|
passwd = &name[ulen];
|
|
plen = *passwd;
|
|
*passwd++ = '\0';
|
|
if (ulen+plen > (int)IBUF_PAP->len-4-2) {
|
|
log_complain(ANAME,
|
|
"received bad PAP password length of %d",
|
|
plen);
|
|
break;
|
|
}
|
|
passwd[plen] = '\0';
|
|
|
|
if (A.our_proto != PPP_PAP) {
|
|
/* Ack it if we did not ask for it */
|
|
pap_acknak(ppp,PAP_CODE_ACK,IBUF_PAP->id,
|
|
"why is this?",
|
|
"for unsolicited PAP request");
|
|
log_complain(ANAME,
|
|
"unsolicited request for PAP username"
|
|
" \"%s\"",
|
|
&IBUF_PAP->d[0]);
|
|
break;
|
|
}
|
|
|
|
/* check it */
|
|
if (!ck_name(ppp, PPP_PAP, name, passwd)) {
|
|
pap_acknak(ppp,PAP_CODE_NAK,IBUF_PAP->id, "wrong","");
|
|
/* no second chance */
|
|
set_tr_msg(&ppp->lcp.fsm, "authentication failure");
|
|
lcp_event(ppp,FSM_CLOSE);
|
|
break;
|
|
}
|
|
|
|
if (ppp->phase != AUTH_PHASE) {
|
|
pap_acknak(ppp,PAP_CODE_ACK,IBUF_PAP->id,
|
|
"why is this?", "for tardy PAP request");
|
|
break;
|
|
}
|
|
|
|
/* Reconfigure and start LCP over if we were using a
|
|
* generic control file entry.
|
|
* Do not send the PAP Ack, because that might fool
|
|
* the peer into advancing to the Network Phase and
|
|
* sending us IPCP packets before we are willing accept
|
|
* them.
|
|
*/
|
|
if (reparse(ppp)) {
|
|
renegotiate(ppp);
|
|
break;
|
|
}
|
|
|
|
A.we_happy = 1;
|
|
A.we_happy_time = clk.tv_sec;
|
|
pap_acknak(ppp, PAP_CODE_ACK, IBUF_PAP->id, "howdy", "");
|
|
|
|
/* send PAP request or CHAP challenge if we now have
|
|
* the the info.
|
|
*/
|
|
if (A.chap_chal_delayed || A.pap_req_delayed)
|
|
auth_time(ppp);
|
|
else
|
|
auth_done(ppp);
|
|
break;
|
|
|
|
|
|
case PAP_CODE_ACK: /* Authenticate-Ack */
|
|
if (ibuf.bits != 0) { /* MP encapsulation is forbidden */
|
|
log_complain(ANAME,"discard MP PAP Request ID=%#x",
|
|
IBUF_PAP->id);
|
|
break;
|
|
}
|
|
ulen = IBUF_PAP->slen;
|
|
name = &IBUF_PAP->d[0];
|
|
if (ulen > (int)IBUF_PAP->len-4) {
|
|
log_complain(ANAME, "received bad PAP Ack message"
|
|
" length of %d, %d too large",
|
|
ulen, ulen - IBUF_PAP->len-4);
|
|
ulen = IBUF_PAP->len-4;
|
|
}
|
|
name[ulen] = '\0';
|
|
|
|
if (A.peer_proto != PPP_PAP) {
|
|
log_complain(ANAME, "discard PAP Ack"
|
|
" without LCP negotiation");
|
|
break;
|
|
}
|
|
|
|
if (IBUF_PAP->id != A.pap_id) {
|
|
log_debug(0,ANAME,
|
|
"wrong PAP Ack ID=%#x != expected %#x,"
|
|
" containing \"%s\"",
|
|
IBUF_PAP->id, A.pap_id, name);
|
|
break;
|
|
}
|
|
A.peer_happy = 1;
|
|
|
|
log_debug(2,ANAME,"receive PAP Ack ID=%#x containing \"%s\"",
|
|
A.pap_id, name);
|
|
|
|
auth_done(ppp);
|
|
break;
|
|
|
|
|
|
case PAP_CODE_NAK: /* Authenticate-Nak */
|
|
if (ibuf.bits != 0) { /* MP encapsulation is forbidden */
|
|
log_complain(ANAME, "discard MP PAP Request ID=%#x",
|
|
IBUF_PAP->id);
|
|
break;
|
|
}
|
|
ulen = IBUF_PAP->slen;
|
|
name = &IBUF_PAP->d[0];
|
|
if (ulen > (int)IBUF_PAP->len-4) {
|
|
log_complain(ANAME,"received bad PAP Nak message"
|
|
" length of %d", ulen);
|
|
break;
|
|
}
|
|
name[ulen] = '\0';
|
|
|
|
if (A.peer_proto != PPP_PAP) {
|
|
log_complain(ANAME, "discard PAP Ack"
|
|
" without LCP negotiation");
|
|
break;
|
|
}
|
|
|
|
if (IBUF_PAP->id != A.pap_id) {
|
|
log_debug(0, ANAME,
|
|
"bad PAP Nak ID %#x != expected %#x,"
|
|
" containing \"%s\"",
|
|
IBUF_PAP->id, A.pap_id, name);
|
|
break;
|
|
}
|
|
log_complain(ANAME,
|
|
"our PAP send_name and send_passwd were"
|
|
" rejected with \"%s\"",
|
|
name,0,0);
|
|
break;
|
|
|
|
default:
|
|
log_ipkt(0, ANAME,"discard bogus PAP packet");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* Compute a standard CHAP challenge value given the random number and
|
|
* the hostname
|
|
*/
|
|
static struct cval *
|
|
chap_chal_val(struct ppp *ppp,
|
|
u_char rnd[CVAL_RND_L],
|
|
char *name)
|
|
{
|
|
int i;
|
|
__uint32_t xor;
|
|
union {
|
|
u_char md5[MD5_DIGEST_LEN];
|
|
u_char digest[CVAL_NAM_L];
|
|
} nm;
|
|
MD5_CTX md5_ctx;
|
|
union { /* local copy to avoid alignment */
|
|
struct cval c; /* hassles */
|
|
__uint32_t i[1];
|
|
} v;
|
|
|
|
|
|
bcopy(rnd, v.c.cval_rnd, sizeof(v.c.cval_rnd));
|
|
|
|
/* digest hostname */
|
|
MD5Init(&md5_ctx);
|
|
MD5Update(&md5_ctx, (u_char *)name, strlen(name));
|
|
MD5Final(nm.md5, &md5_ctx);
|
|
bcopy(nm.digest, v.c.cval_nam, sizeof(v.c.cval_nam));
|
|
|
|
/* pick direction */
|
|
switch (ppp->dv.callmode) {
|
|
case CALLEE:
|
|
case RE_CALLEE:
|
|
bcopy("rECV", v.c.cval_dir, sizeof(v.c.cval_dir));
|
|
break;
|
|
case CALLER:
|
|
case RE_CALLER:
|
|
case Q_CALLER:
|
|
bcopy("MaDe", v.c.cval_dir, sizeof(v.c.cval_dir));
|
|
break;
|
|
default:
|
|
bcopy("Oops", v.c.cval_dir, sizeof(v.c.cval_dir));
|
|
break;
|
|
}
|
|
|
|
/* combine direction and digested hostname */
|
|
for (i = 0, xor = 0; i < sizeof(v.c)/4; i++)
|
|
xor ^= v.i[i];
|
|
bcopy(&xor, &v.c.cval_dir, sizeof(v.c.cval_dir));
|
|
|
|
return &v.c;
|
|
}
|
|
|
|
|
|
/* handle incoming CHAP packets
|
|
*/
|
|
void
|
|
chap_ipkt(struct ppp *ppp)
|
|
{
|
|
int len;
|
|
u_char respond_val[CHAP_RESP_MD5_LEN];
|
|
MD5_CTX md5_ctx;
|
|
char *name;
|
|
int need_renegotiate;
|
|
|
|
|
|
if (ibuf_info_len < (int)IBUF_CHAP->len) {
|
|
log_complain(ANAME,"discard CHAP packet with %d bytes "
|
|
"but claiming %d",
|
|
ibuf_info_len, IBUF_CHAP->len);
|
|
return;
|
|
}
|
|
/* ensure any names are null terminated */
|
|
IBUF_CHAP->u.msg[IBUF_CHAP->len] = '\0';
|
|
|
|
if (ppp->phase != AUTH_PHASE
|
|
&& ppp->phase != NET_PHASE) {
|
|
log_debug(2,ANAME,"discard CHAP packet received in %s phase",
|
|
phase_name(ppp->phase));
|
|
return;
|
|
}
|
|
|
|
switch (IBUF_CHAP->code) {
|
|
case CHAP_CODE_CHALLENGE:
|
|
if (ibuf.bits != 0) { /* MP encapsulation is forbidden */
|
|
log_complain(ANAME,"discard MP CHAP Request ID=%#x",
|
|
IBUF_CHAP->id);
|
|
break;
|
|
}
|
|
len = IBUF_CHAP->u.chal.vsize;
|
|
if (len > (int)IBUF_CHAP->len - (4+2)) {
|
|
log_complain(ANAME,
|
|
"receive bad CHAP challenge value-size=%d"
|
|
"; overall len=%d",
|
|
len, IBUF_CHAP->len);
|
|
break;
|
|
}
|
|
name = ((char *)&IBUF_CHAP->u.chal.v)+len;
|
|
|
|
if (A.peer_proto != PPP_CHAP) {
|
|
log_complain(ANAME, "discard CHAP challenge"
|
|
" without LCP negotiation");
|
|
break;
|
|
}
|
|
|
|
log_debug(2,ANAME,"receive CHAP challenge ID=%#x,"
|
|
" name \"%s\"",
|
|
IBUF_CHAP->id, name);
|
|
|
|
/* Check the name. If it is bad, there is no way to
|
|
* tell the peer except by letting it time-out.
|
|
*/
|
|
if (!ck_name(ppp,PPP_CHAP,name,0))
|
|
break;
|
|
|
|
/* Reconfigure if we were using a generic control file entry.
|
|
* This may get us a password with which to respond.
|
|
*/
|
|
need_renegotiate = reparse(ppp);
|
|
|
|
if (!A.have_send_passwd) {
|
|
log_debug(1,ANAME, "receive CHAP challenge"
|
|
" for name \"%s\", but have no send_passwd"
|
|
"--assume null secret",
|
|
name);
|
|
}
|
|
|
|
/* If we must renegotiate LCP, do not send the CHAP
|
|
* response, because that might fool the peer into
|
|
* advancing to the Network Phase and sending
|
|
* us IPCP packets before we are willing accept them.
|
|
*/
|
|
if (need_renegotiate) {
|
|
renegotiate(ppp);
|
|
break;
|
|
}
|
|
|
|
/* Check to see if the peer is trying to us as an oracle,
|
|
* feeding our own challenges back to us.
|
|
* If so, ignore its challenges.
|
|
*/
|
|
if (len == sizeof(A.chap_cval)
|
|
&& !bcmp(chap_chal_val(ppp,
|
|
IBUF_CHAP->u.chal.v.cval_rnd,
|
|
name),
|
|
&IBUF_CHAP->u.chal.v,
|
|
sizeof(IBUF_CHAP->u.chal.v))) {
|
|
log_complain(ANAME, "received our own CHAP challenge"
|
|
"--do not respond");
|
|
break;
|
|
}
|
|
|
|
log_debug(2,ANAME,
|
|
"answer CHAP challenge with our send_name \"%s\"",
|
|
A.send_name);
|
|
|
|
OBUF_CHAP->code = CHAP_CODE_RESPONSE;
|
|
OBUF_CHAP->id = IBUF_CHAP->id;
|
|
OBUF_CHAP->len = (4+1+CHAP_RESP_MD5_LEN+strlen(A.send_name));
|
|
obuf.bits = 0;
|
|
obuf.proto = PPP_CHAP;
|
|
OBUF_CHAP->u.resp.vsize = CHAP_RESP_MD5_LEN;
|
|
MD5Init(&md5_ctx);
|
|
MD5Update(&md5_ctx, &OBUF_CHAP->id,
|
|
sizeof(OBUF_CHAP->id));
|
|
if (A.have_send_passwd)
|
|
MD5Update(&md5_ctx, (u_char*)A.send_passwd,
|
|
strlen(A.send_passwd));
|
|
MD5Update(&md5_ctx, (u_char*)&IBUF_CHAP->u.chal.v, len);
|
|
MD5Final(OBUF_CHAP->u.resp.val,&md5_ctx);
|
|
(void)strcpy(OBUF_CHAP->u.resp.name, A.send_name);
|
|
ppp_send(ppp,&obuf,OBUF_CHAP->len);
|
|
|
|
/* send PAP request or CHAP challenge if we now have the info.
|
|
*/
|
|
if (A.chap_chal_delayed || A.pap_req_delayed)
|
|
auth_time(ppp);
|
|
else
|
|
auth_done(ppp);
|
|
break;
|
|
|
|
|
|
case CHAP_CODE_RESPONSE:
|
|
if (ibuf.bits != 0) { /* MP encapsulation is forbidden */
|
|
log_complain(ANAME,"discard MP CHAP Request ID=%#x",
|
|
IBUF_CHAP->id);
|
|
break;
|
|
}
|
|
len = IBUF_CHAP->u.resp.vsize;
|
|
if (len != CHAP_RESP_MD5_LEN
|
|
|| len > (int)IBUF_CHAP->len - (4+2)) {
|
|
log_complain(ANAME,
|
|
"received bad CHAP response"
|
|
" value-size=%d; overall len=%d",
|
|
len, IBUF_CHAP->len);
|
|
break;
|
|
}
|
|
if (A.our_proto != PPP_CHAP) {
|
|
log_complain(ANAME, "discard CHAP response"
|
|
" without LCP negotiation");
|
|
break;
|
|
}
|
|
|
|
if (IBUF_CHAP->id != A.chap_id) {
|
|
/* This can happen if one machine or the other
|
|
* gets behind.
|
|
*/
|
|
log_debug(2,ANAME,
|
|
"wrong CHAP response ID=%#x != expected %#x",
|
|
IBUF_CHAP->id, A.chap_id);
|
|
break;
|
|
}
|
|
|
|
name = (char*)&IBUF_CHAP->u.resp.val[len];
|
|
|
|
log_debug(2,ANAME,"receive CHAP Response ID=%#x, name \"%s\"",
|
|
A.chap_id, name);
|
|
|
|
/* check the name */
|
|
if (!ck_name(ppp,PPP_CHAP,name,0)) {
|
|
chap_acknak(ppp, CHAP_CODE_FAILURE, A.chap_id);
|
|
break;
|
|
}
|
|
|
|
/* Reconfigure if we were using a generic control file entry.
|
|
* This might find a usable secret.
|
|
*/
|
|
need_renegotiate = reparse(ppp);
|
|
|
|
/* check the secret */
|
|
MD5Init(&md5_ctx);
|
|
MD5Update(&md5_ctx, &A.chap_id, sizeof(A.chap_id));
|
|
if (A.recv_passwd[0] != '\0') {
|
|
MD5Update(&md5_ctx, (u_char*)A.recv_passwd,
|
|
strlen(A.recv_passwd));
|
|
} else {
|
|
log_complain(ANAME, "receive CHAP response for \"%s\","
|
|
" but recv_passwd not specified",
|
|
name);
|
|
break;
|
|
}
|
|
MD5Update(&md5_ctx,
|
|
(u_char*)&A.chap_cval, sizeof(A.chap_cval));
|
|
MD5Final(respond_val,&md5_ctx);
|
|
if (bcmp(respond_val, IBUF_CHAP->u.resp.val,
|
|
sizeof(respond_val))) {
|
|
chap_acknak(ppp, CHAP_CODE_FAILURE, A.chap_id);
|
|
break;
|
|
}
|
|
|
|
/* If we must renegotiate LCP, do not send the CHAP
|
|
* response, because that might fool the peer into
|
|
* advancing to the Network Phase and sending
|
|
* us IPCP packets before we are willing accept them.
|
|
*/
|
|
if (need_renegotiate) {
|
|
renegotiate(ppp);
|
|
break;
|
|
}
|
|
|
|
A.we_happy = 1;
|
|
A.we_happy_time = clk.tv_sec;
|
|
chap_acknak(ppp, CHAP_CODE_SUCCESS, A.chap_id);
|
|
|
|
/* send PAP request or CHAP challenge if we now have the info.
|
|
*/
|
|
if (A.chap_chal_delayed || A.pap_req_delayed)
|
|
auth_time(ppp);
|
|
else
|
|
auth_done(ppp);
|
|
break;
|
|
|
|
|
|
case CHAP_CODE_SUCCESS:
|
|
if (ibuf.bits != 0) { /* MP encapsulation is forbidden */
|
|
log_complain(ANAME,"discard MP CHAP Request ID=%#x",
|
|
IBUF_CHAP->id);
|
|
break;
|
|
}
|
|
if (A.peer_proto != PPP_CHAP) {
|
|
log_complain(ANAME, "discard CHAP success"
|
|
" without LCP negotiation");
|
|
break;
|
|
}
|
|
|
|
A.peer_happy = 1;
|
|
|
|
log_debug(2,ANAME,"receive CHAP Success ID=%#x"
|
|
" containing \"%s\"",
|
|
IBUF_CHAP->id, IBUF_CHAP->u.msg);
|
|
|
|
auth_done(ppp);
|
|
break;
|
|
|
|
case CHAP_CODE_FAILURE:
|
|
if (ibuf.bits != 0) { /* MP encapsulation is forbidden */
|
|
log_complain(ANAME,"discard MP CHAP Request ID=%#x",
|
|
IBUF_CHAP->id);
|
|
break;
|
|
}
|
|
if (A.peer_proto != PPP_CHAP) {
|
|
log_complain(ANAME, "discard CHAP success"
|
|
" without LCP negotiation");
|
|
break;
|
|
}
|
|
|
|
log_complain(ANAME,
|
|
"our CHAP send_name and send_passwd were"
|
|
" rejected with \"%s\"",
|
|
IBUF_CHAP->u.msg);
|
|
break;
|
|
|
|
default:
|
|
log_ipkt(0, ANAME,"discard bogus CHAP packet");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* Send a (or another) CHAP challenge to get the peer to authenticate
|
|
* itself to us.
|
|
* If we do not know our name, because we are hoping to figure
|
|
* it out after the peer tells us its name and we reconfigure,
|
|
* then delay until the peer sends us a name and we reconfigure.
|
|
* But do not delay if the peer is not going to send us a CHAP
|
|
* challenge or PAP request.
|
|
*/
|
|
static void
|
|
send_chap(struct ppp *ppp)
|
|
{
|
|
struct {
|
|
struct timeval clk;
|
|
struct timeval cur_date;
|
|
__uint32_t unit;
|
|
__uint32_t locaddr;
|
|
__uint32_t remaddr;
|
|
__uint32_t rand;
|
|
} val;
|
|
union {
|
|
u_char md5[MD5_DIGEST_LEN];
|
|
u_char val[CVAL_RND_L];
|
|
} rnd;
|
|
MD5_CTX md5_ctx;
|
|
|
|
|
|
if (A.our_proto != PPP_CHAP || A.we_happy)
|
|
return;
|
|
|
|
if (reconfigure
|
|
&& A.send_name[0] == '\0'
|
|
&& !A.peer_happy) {
|
|
A.chap_chal_delayed = 1;
|
|
return;
|
|
}
|
|
|
|
A.chap_chal_delayed = 0;
|
|
if (A.send_name[0] == '\0') {
|
|
log_debug(1,ANAME,"default our send_name to %s",
|
|
ourhost_nam);
|
|
(void)strcpy(ppp0.auth.send_name,ourhost_nam);
|
|
}
|
|
|
|
/* build the challenge value */
|
|
bzero(&A.chap_cval, sizeof(A.chap_cval));
|
|
val.clk = clk;
|
|
val.cur_date = cur_date;
|
|
val.unit = ppp->dv.unit;
|
|
val.remaddr = remhost.sin_addr.s_addr;
|
|
val.locaddr = lochost.sin_addr.s_addr;
|
|
val.rand = newmagic();
|
|
MD5Init(&md5_ctx);
|
|
MD5Update(&md5_ctx, (u_char*)&val, sizeof(val));
|
|
MD5Final(rnd.md5, &md5_ctx);
|
|
bcopy(rnd.val, A.chap_cval.cval_rnd, sizeof(A.chap_cval.cval_rnd));
|
|
|
|
bcopy(chap_chal_val(ppp, rnd.val, A.send_name),
|
|
&A.chap_cval, sizeof(A.chap_cval));
|
|
|
|
OBUF_CHAP->code = CHAP_CODE_CHALLENGE;
|
|
OBUF_CHAP->id = ++A.chap_id;
|
|
OBUF_CHAP->u.chal.vsize = sizeof(A.chap_cval);
|
|
bcopy(&A.chap_cval, &OBUF_CHAP->u.chal.v, sizeof(A.chap_cval));
|
|
(void)strcpy(OBUF_CHAP->u.chal.name, A.send_name);
|
|
obuf.bits = 0;
|
|
obuf.proto = PPP_CHAP;
|
|
OBUF_CHAP->len = (4+1+sizeof(A.chap_cval)+strlen(A.send_name));
|
|
log_debug(2,ANAME,"send CHAP challenge ID=%#x, name \"%s\"",
|
|
A.chap_id, A.send_name);
|
|
|
|
ppp_send(ppp,&obuf,OBUF_CHAP->len);
|
|
}
|
|
|
|
|
|
/* Send another PAP request asking the peer to recognize us,
|
|
* but only if we know what authentication to use.
|
|
* If we are waiting for the peer to go first so we can reconfigure
|
|
* and figure out the username or password, then just delay.
|
|
*
|
|
* If necessary, use a null PAP password.
|
|
*/
|
|
static void
|
|
send_pap(struct ppp *ppp)
|
|
{
|
|
register int ulen, plen;
|
|
|
|
if (A.peer_proto != PPP_PAP || A.peer_happy)
|
|
return;
|
|
|
|
if (reconfigure
|
|
&& (A.send_name[0] == '\0' || !A.have_send_passwd)) {
|
|
A.pap_req_delayed = 1;
|
|
return;
|
|
}
|
|
|
|
A.pap_req_delayed = 0;
|
|
if (A.send_name[0] == '\0') {
|
|
log_debug(1,ANAME,"default send_name to %s",
|
|
ourhost_nam);
|
|
(void)strcpy(ppp0.auth.send_name,ourhost_nam);
|
|
}
|
|
|
|
OBUF_PAP->code = PAP_CODE_REQ;
|
|
OBUF_PAP->id = A.pap_id;
|
|
ulen = strlen(A.send_name);
|
|
OBUF_PAP->slen = ulen;
|
|
(void)strcpy(&OBUF_PAP->d[0], A.send_name);
|
|
if (!A.have_send_passwd) {
|
|
plen = 0;
|
|
log_debug(1,ANAME,"'send_pap' specified but"
|
|
" 'send_passwd' not specified--"
|
|
"assume null password");
|
|
} else {
|
|
plen = strlen(A.send_passwd);
|
|
}
|
|
OBUF_PAP->d[ulen] = plen;
|
|
(void)strcpy(&OBUF_PAP->d[ulen+1], A.send_passwd);
|
|
obuf.bits = 0;
|
|
obuf.proto = PPP_PAP;
|
|
OBUF_PAP->len = ulen+plen+sizeof(struct pap_pkt);
|
|
log_debug(2,ANAME,"send PAP request ID=%#x",A.pap_id);
|
|
ppp_send(ppp,&obuf,OBUF_PAP->len);
|
|
}
|
|
|
|
|
|
/* do something when the authorization timer expires
|
|
*/
|
|
void
|
|
auth_time(struct ppp *ppp)
|
|
{
|
|
int secs, secs2;
|
|
|
|
|
|
/* Do reauthentication during the network phase if appropriate.
|
|
*/
|
|
if (ppp->phase != AUTH_PHASE) {
|
|
if (A.reauth_secs == 0
|
|
|| A.our_proto != PPP_CHAP
|
|
|| ppp->phase != NET_PHASE) {
|
|
A.timer.tv_sec = TIME_NEVER;
|
|
return;
|
|
}
|
|
|
|
if (A.we_happy) {
|
|
/* sleep if not time to reauthenticate */
|
|
secs = A.we_happy_time+A.reauth_secs - clk.tv_sec;
|
|
if (secs > 0) {
|
|
fsm_settimer(&A.timer, secs);
|
|
return;
|
|
}
|
|
|
|
/* start to reauthenticate if it is time */
|
|
A.we_happy = 0;
|
|
A.stime = clk.tv_sec;
|
|
}
|
|
}
|
|
|
|
|
|
/* Quit if no authentication received in time.
|
|
*/
|
|
secs = clk.tv_sec - A.stime;
|
|
secs2 = A.max_secs - secs;
|
|
if (secs2 <= 0) {
|
|
if (!A.we_happy) {
|
|
log_complain(ANAME, "no %s received after %d seconds",
|
|
((A.our_proto == PPP_PAP)
|
|
? "PAP username/password"
|
|
: "CHAP response"),
|
|
secs);
|
|
A.timer.tv_sec = TIME_NEVER;
|
|
set_tr_msg(&ppp->lcp.fsm, "no authentication");
|
|
lcp_event(ppp,FSM_CLOSE);
|
|
return;
|
|
}
|
|
|
|
if (!A.peer_happy) {
|
|
if (A.peer_proto == PPP_PAP) {
|
|
log_complain(ANAME, "%s did not like our"
|
|
" PAP requests; forget about"
|
|
" authentication", remote);
|
|
} else {
|
|
log_complain(ANAME, "no usable CHAP challenge"
|
|
" received from %s; forget about"
|
|
" autentication", remote);
|
|
}
|
|
A.peer_happy = 1;
|
|
}
|
|
}
|
|
|
|
if (!A.we_happy || !A.peer_happy) {
|
|
if (secs > 0)
|
|
log_debug(2,ANAME,
|
|
"TO+ %d seconds remaining after %d seconds",
|
|
secs2, secs);
|
|
|
|
/* Send another CHAP challenge to get the peer to authenticate
|
|
* itself to us.
|
|
*/
|
|
send_chap(ppp);
|
|
/* Send another PAP request asking the peer to recognize us
|
|
*/
|
|
send_pap(ppp);
|
|
}
|
|
|
|
/* See if that finished things, and if so, set the timer for
|
|
* re-authentication
|
|
*/
|
|
auth_done(ppp);
|
|
fsm_settimer(&A.timer, A.ms);
|
|
}
|