Module Name:    src
Committed By:   yamaguchi
Date:           Wed Nov 25 09:46:05 UTC 2020

Modified Files:
        src/sys/net: if_spppsubr.c if_spppvar.h

Log Message:
implement auth protocols on the state-machine of control protocols

reviewed by knakahara@n.o.


To generate a diff of this commit:
cvs rdiff -u -r1.200 -r1.201 src/sys/net/if_spppsubr.c
cvs rdiff -u -r1.28 -r1.29 src/sys/net/if_spppvar.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/if_spppsubr.c
diff -u src/sys/net/if_spppsubr.c:1.200 src/sys/net/if_spppsubr.c:1.201
--- src/sys/net/if_spppsubr.c:1.200	Wed Nov 25 09:41:20 2020
+++ src/sys/net/if_spppsubr.c	Wed Nov 25 09:46:05 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_spppsubr.c,v 1.200 2020/11/25 09:41:20 yamaguchi Exp $	 */
+/*	$NetBSD: if_spppsubr.c,v 1.201 2020/11/25 09:46:05 yamaguchi Exp $	 */
 
 /*
  * Synchronous PPP/Cisco link level subroutines.
@@ -41,7 +41,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_spppsubr.c,v 1.200 2020/11/25 09:41:20 yamaguchi Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_spppsubr.c,v 1.201 2020/11/25 09:46:05 yamaguchi Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_inet.h"
@@ -102,6 +102,9 @@ __KERNEL_RCSID(0, "$NetBSD: if_spppsubr.
 #define	DEFAULT_NORECV_TIME		15	/* before we get worried */
 #define DEFAULT_MAX_AUTH_FAILURES	5	/* max. auth. failures */
 
+#define FAILMSG "Failed..."
+#define SUCCMSG "Welcome!"
+
 /*
  * Interface flags that can be set in an ifconfig command.
  *
@@ -254,6 +257,12 @@ struct cp {
 	void	(*scan)(const struct cp *, struct sppp *);
 };
 
+enum auth_role {
+	SPPP_AUTH_NOROLE = 0,
+	SPPP_AUTH_SERV = __BIT(0),
+	SPPP_AUTH_PEER = __BIT(1),
+};
+
 static struct sppp *spppq;
 static kmutex_t *spppq_lock = NULL;
 static callout_t keepalive_ch;
@@ -325,6 +334,8 @@ static void sppp_cp_to_ipcp(void *);
 static void sppp_cp_to_ipv6cp(void *);
 static void sppp_auth_send(const struct cp *, struct sppp *,
 			    unsigned int, unsigned int, ...);
+static int sppp_auth_role(const struct cp *, struct sppp *);
+static void sppp_auth_to_event(const struct cp *, struct sppp *);
 
 static void sppp_up_event(struct sppp *, void *);
 static void sppp_down_event(struct sppp *, void *);
@@ -337,7 +348,6 @@ static void sppp_rcn_event(struct sppp *
 static void sppp_rtr_event(struct sppp *, void *);
 static void sppp_rta_event(struct sppp *, void *);
 static void sppp_rxj_event(struct sppp *, void *);
-static void sppp_null_event(struct sppp *, void *);
 
 static void sppp_null(struct sppp *);
 static void sppp_sca_scn(const struct cp *, struct sppp *);
@@ -359,6 +369,7 @@ static void sppp_lcp_tlf(struct sppp *);
 static void sppp_lcp_scr(struct sppp *);
 static void sppp_lcp_check_and_close(struct sppp *);
 static int sppp_ncp_check(struct sppp *);
+static int sppp_auth_check(struct sppp *);
 
 static void sppp_ipcp_init(struct sppp *);
 static void sppp_ipcp_up(struct sppp *, void *);
@@ -392,22 +403,31 @@ static void sppp_ipv6cp_scr(struct sppp 
 
 static void sppp_pap_input(struct sppp *, struct mbuf *);
 static void sppp_pap_init(struct sppp *);
+static void sppp_pap_up(struct sppp *, void *);
+static void sppp_pap_down(struct sppp *, void *);
 static void sppp_pap_open(struct sppp *, void *);
 static void sppp_pap_close(struct sppp *, void *);
-static void sppp_pap_TO(void *);
-static void sppp_pap_my_TO(void *);
+static void sppp_pap_TO(struct sppp *, void *);
+static void sppp_pap_tls(struct sppp *);
+static void sppp_pap_tlf(struct sppp *);
 static void sppp_pap_tlu(struct sppp *);
-static void sppp_pap_tld(struct sppp *);
 static void sppp_pap_scr(struct sppp *);
+static void sppp_pap_scr(struct sppp *);
+static void sppp_pap_scan(const struct cp *, struct sppp *);
 
 static void sppp_chap_input(struct sppp *, struct mbuf *);
 static void sppp_chap_init(struct sppp *);
+static void sppp_chap_up(struct sppp *, void *);
+static void sppp_chap_down(struct sppp *, void *);
 static void sppp_chap_open(struct sppp *, void *);
 static void sppp_chap_close(struct sppp *, void *);
-static void sppp_chap_TO(void *);
+static void sppp_chap_TO(struct sppp *, void *);
 static void sppp_chap_tlu(struct sppp *);
-static void sppp_chap_tld(struct sppp *);
+static void sppp_chap_tls(struct sppp *);
+static void sppp_chap_tlf(struct sppp *);
 static void sppp_chap_scr(struct sppp *);
+static void sppp_chap_scan(const struct cp *, struct sppp *);
+static void sppp_chap_rcv_challenge_event(struct sppp *, void *);
 
 static const char *sppp_auth_type_name(u_short, u_char);
 static const char *sppp_cp_type_name(u_char);
@@ -494,18 +514,18 @@ static const struct cp ipv6cp = {
 
 static const struct cp pap = {
 	PPP_PAP, IDX_PAP, CP_AUTH, "pap",
-	sppp_null_event, sppp_null_event, sppp_pap_open, sppp_pap_close,
-	sppp_null_event, 0, 0, 0,
-	sppp_pap_tlu, sppp_pap_tld, sppp_null, sppp_null,
-	sppp_pap_scr, 0
+	sppp_pap_up, sppp_pap_down, sppp_pap_open, sppp_pap_close,
+	sppp_pap_TO, 0, 0, 0,
+	sppp_pap_tlu, sppp_null, sppp_pap_tls, sppp_pap_tlf,
+	sppp_pap_scr, sppp_pap_scan
 };
 
 static const struct cp chap = {
 	PPP_CHAP, IDX_CHAP, CP_AUTH, "chap",
-	sppp_null_event, sppp_null_event, sppp_chap_open, sppp_chap_close,
-	sppp_null_event, 0, 0, 0,
-	sppp_chap_tlu, sppp_chap_tld, sppp_null, sppp_null,
-	sppp_chap_scr, 0
+	sppp_chap_up, sppp_chap_down, sppp_chap_open, sppp_chap_close,
+	sppp_chap_TO, 0, 0, 0,
+	sppp_chap_tlu, sppp_null, sppp_chap_tls, sppp_chap_tlf,
+	sppp_chap_scr, sppp_chap_scan
 };
 
 static const struct cp *cps[IDX_COUNT] = {
@@ -1074,13 +1094,12 @@ sppp_detach(struct ifnet *ifp)
 
 	sppp_cp_fini(&lcp, sp);
 	sppp_cp_fini(&ipcp, sp);
-	callout_stop(&sp->scp[IDX_PAP].ch);
-	callout_stop(&sp->scp[IDX_CHAP].ch);
+	sppp_cp_fini(&pap, sp);
+	sppp_cp_fini(&chap, sp);
 #ifdef INET6
 	sppp_cp_fini(&ipv6cp, sp);
 #endif
 	sppp_wq_destroy(sp, sp->wq_cp);
-	callout_stop(&sp->pap_my_to_ch);
 
 	/* free authentication info */
 	if (sp->myauth.name) free(sp->myauth.name, M_DEVBUF);
@@ -1523,6 +1542,23 @@ sppp_cp_to_ipv6cp(void *xsp)
 }
 
 static void
+sppp_cp_to_pap(void *xsp)
+{
+	struct sppp *sp = xsp;
+
+	sppp_wq_add(sp->wq_cp, &sp->scp[IDX_PAP].work_to);
+}
+
+static void
+sppp_cp_to_chap(void *xsp)
+{
+	struct sppp *sp = xsp;
+
+	sppp_wq_add(sp->wq_cp, &sp->scp[IDX_CHAP].work_to);
+}
+
+
+static void
 sppp_cp_init(const struct cp *cp, struct sppp *sp)
 {
 	struct sppp_cp *scp;
@@ -1531,6 +1567,8 @@ sppp_cp_init(const struct cp *cp, struct
 		[IDX_LCP] = sppp_cp_to_lcp,
 		[IDX_IPCP] = sppp_cp_to_ipcp,
 		[IDX_IPV6CP] = sppp_cp_to_ipv6cp,
+		[IDX_PAP] = sppp_cp_to_pap,
+		[IDX_CHAP] = sppp_cp_to_chap,
 	};
 
 	scp = &sp->scp[cp->protoidx];
@@ -1814,6 +1852,10 @@ sppp_up_event(struct sppp *sp, void *xcp
 
 	KASSERT(SPPP_WLOCKED(sp));
 
+	if ((cp->flags & CP_AUTH) != 0 &&
+	    sppp_auth_role(cp, sp) == SPPP_AUTH_NOROLE)
+		return;
+
 	if (debug)
 		log(LOG_DEBUG, "%s: %s up(%s)\n",
 		    ifp->if_xname, cp->name,
@@ -1843,6 +1885,10 @@ sppp_down_event(struct sppp *sp, void *x
 
 	KASSERT(SPPP_WLOCKED(sp));
 
+	if ((cp->flags & CP_AUTH) != 0 &&
+	    sppp_auth_role(cp, sp) == SPPP_AUTH_NOROLE)
+		return;
+
 	if (debug)
 		log(LOG_DEBUG, "%s: %s down(%s)\n",
 		    ifp->if_xname, cp->name,
@@ -1882,6 +1928,10 @@ sppp_open_event(struct sppp *sp, void *x
 
 	KASSERT(SPPP_WLOCKED(sp));
 
+	if ((cp->flags & CP_AUTH) != 0 &&
+	    sppp_auth_role(cp, sp) == SPPP_AUTH_NOROLE)
+		return;
+
 	if (debug)
 		log(LOG_DEBUG, "%s: %s open(%s)\n",
 		    ifp->if_xname, cp->name,
@@ -1921,6 +1971,10 @@ sppp_close_event(struct sppp *sp, void *
 
 	KASSERT(SPPP_WLOCKED(sp));
 
+	if ((cp->flags & CP_AUTH) != 0 &&
+	    sppp_auth_role(cp, sp) == SPPP_AUTH_NOROLE)
+		return;
+
 	if (debug)
 		log(LOG_DEBUG, "%s: %s close(%s)\n",
 		    ifp->if_xname, cp->name,
@@ -1948,8 +2002,10 @@ sppp_close_event(struct sppp *sp, void *
 	case STATE_ACK_RCVD:
 	case STATE_ACK_SENT:
 		sp->scp[cp->protoidx].rst_counter = sp->lcp.max_terminate;
-		sppp_cp_send(sp, cp->proto, TERM_REQ,
-		    ++sp->scp[cp->protoidx].seq, 0, 0);
+		if ((cp->flags & CP_AUTH) == 0) {
+			sppp_cp_send(sp, cp->proto, TERM_REQ,
+			    ++sp->scp[cp->protoidx].seq, 0, 0);
+		}
 		sppp_cp_change_state(cp, sp, STATE_CLOSING);
 		break;
 	}
@@ -1976,21 +2032,18 @@ sppp_to_event(struct sppp *sp, void *xcp
 		/* TO- event */
 		switch (sp->scp[cp->protoidx].state) {
 		case STATE_CLOSING:
-			(cp->tlf)(sp);
 			sppp_cp_change_state(cp, sp, STATE_CLOSED);
-			sppp_lcp_check_and_close(sp);
+			(cp->tlf)(sp);
 			break;
 		case STATE_STOPPING:
-			(cp->tlf)(sp);
 			sppp_cp_change_state(cp, sp, STATE_STOPPED);
-			sppp_lcp_check_and_close(sp);
+			(cp->tlf)(sp);
 			break;
 		case STATE_REQ_SENT:
 		case STATE_ACK_RCVD:
 		case STATE_ACK_SENT:
-			(cp->tlf)(sp);
 			sppp_cp_change_state(cp, sp, STATE_STOPPED);
-			sppp_lcp_check_and_close(sp);
+			(cp->tlf)(sp);
 			break;
 		}
 	else
@@ -1998,8 +2051,10 @@ sppp_to_event(struct sppp *sp, void *xcp
 		switch (sp->scp[cp->protoidx].state) {
 		case STATE_CLOSING:
 		case STATE_STOPPING:
-			sppp_cp_send(sp, cp->proto, TERM_REQ,
-			    ++sp->scp[cp->protoidx].seq, 0, 0);
+			if ((cp->flags & CP_AUTH) == 0) {
+				sppp_cp_send(sp, cp->proto, TERM_REQ,
+				    ++sp->scp[cp->protoidx].seq, 0, 0);
+			}
 			callout_schedule(&sp->scp[cp->protoidx].ch, sp->lcp.timeout);
 			break;
 		case STATE_REQ_SENT:
@@ -2067,8 +2122,10 @@ sppp_rcr_event(struct sppp *sp, void *xc
 			}
 			break;
 		case STATE_CLOSED:
-			sppp_cp_send(sp, cp->proto, TERM_ACK,
-			    sp->scp[cp->protoidx].rconfid, 0, 0);
+			if ((cp->flags & CP_AUTH) == 0) {
+				sppp_cp_send(sp, cp->proto, TERM_ACK,
+				    sp->scp[cp->protoidx].rconfid, 0, 0);
+			}
 			break;
 		default:
 			printf("%s: %s illegal RCR+ in state %s\n",
@@ -2109,8 +2166,10 @@ sppp_rcr_event(struct sppp *sp, void *xc
 			break;
 		case STATE_CLOSED:
 			sppp_cp_change_state(cp, sp, STATE_CLOSED);
-			sppp_cp_send(sp, cp->proto, TERM_ACK,
-			    sp->scp[cp->protoidx].rconfid, 0, 0);
+			if ((cp->flags & CP_AUTH) == 0) {
+				sppp_cp_send(sp, cp->proto, TERM_ACK,
+				    sp->scp[cp->protoidx].rconfid, 0, 0);
+			}
 			break;
 		default:
 			printf("%s: %s illegal RCR- in state %s\n",
@@ -2130,8 +2189,10 @@ sppp_rca_event(struct sppp *sp, void *xc
 	switch (sp->scp[cp->protoidx].state) {
 	case STATE_CLOSED:
 	case STATE_STOPPED:
-		sppp_cp_send(sp, cp->proto, TERM_ACK,
-		    sp->scp[cp->protoidx].rconfid, 0, 0);
+		if ((cp->flags & CP_AUTH) == 0) {
+			sppp_cp_send(sp, cp->proto, TERM_ACK,
+			    sp->scp[cp->protoidx].rconfid, 0, 0);
+		}
 		break;
 	case STATE_CLOSING:
 	case STATE_STOPPING:
@@ -2172,8 +2233,10 @@ sppp_rcn_event(struct sppp *sp, void *xc
 	switch (sp->scp[cp->protoidx].state) {
 	case STATE_CLOSED:
 	case STATE_STOPPED:
-		sppp_cp_send(sp, cp->proto, TERM_ACK,
-		    sp->scp[cp->protoidx].rconfid, 0, 0);
+		if ((cp->flags & CP_AUTH) == 0) {
+			sppp_cp_send(sp, cp->proto, TERM_ACK,
+			    sp->scp[cp->protoidx].rconfid, 0, 0);
+		}
 		break;
 	case STATE_REQ_SENT:
 	case STATE_ACK_SENT:
@@ -2232,8 +2295,10 @@ sppp_rtr_event(struct sppp *sp, void *xc
 	if (debug)
 		log(LOG_DEBUG, "%s: %s send terminate-ack\n",
 		    ifp->if_xname, cp->name);
-	sppp_cp_send(sp, cp->proto, TERM_ACK,
-	    sp->scp[cp->protoidx].rseq, 0, 0);
+	if ((cp->flags & CP_AUTH) == 0) {
+		sppp_cp_send(sp, cp->proto, TERM_ACK,
+		    sp->scp[cp->protoidx].rseq, 0, 0);
+	}
 }
 
 static void
@@ -2249,14 +2314,12 @@ sppp_rta_event(struct sppp *sp, void *xc
 	case STATE_ACK_SENT:
 		break;
 	case STATE_CLOSING:
-		(cp->tlf)(sp);
 		sppp_cp_change_state(cp, sp, STATE_CLOSED);
-		sppp_lcp_check_and_close(sp);
+		(cp->tlf)(sp);
 		break;
 	case STATE_STOPPING:
-		(cp->tlf)(sp);
 		sppp_cp_change_state(cp, sp, STATE_STOPPED);
-		sppp_lcp_check_and_close(sp);
+		(cp->tlf)(sp);
 		break;
 	case STATE_ACK_RCVD:
 		sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
@@ -3005,7 +3068,6 @@ sppp_lcp_tlu(struct sppp *sp)
 {
 	struct ifnet *ifp = &sp->pp_if;
 	int i;
-	uint32_t mask;
 
 	KASSERT(SPPP_WLOCKED(sp));
 
@@ -3037,39 +3099,33 @@ sppp_lcp_tlu(struct sppp *sp)
 	 * don't do anything when they get an Open event.
 	 */
 	for (i = 0; i < IDX_COUNT; i++)
-		if ((cps[i])->flags & CP_AUTH)
-			(cps[i])->Open(sp, __UNCONST(&cps[i]));
+		if ((cps[i])->flags & CP_AUTH) {
+			sppp_wq_add(sp->wq_cp,
+			    &sp->scp[(cps[i])->protoidx].work_open);
+		}
 
 	if (sp->pp_phase == SPPP_PHASE_NETWORK) {
 		/* Notify all NCPs. */
 		for (i = 0; i < IDX_COUNT; i++)
-			if ((cps[i])->flags & CP_NCP)
-				(cps[i])->Open(sp, __UNCONST(&cps[i]));
-	}
-
-	/* Send Up events to all started protos. */
-	for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) {
-		if ((sp->lcp.protos & mask) && ((cps[i])->flags & CP_LCP) == 0) {
-			(cps[i])->Up(sp, __UNCONST(&cps[i]));
-		}
+			if ((cps[i])->flags & CP_NCP) {
+			sppp_wq_add(sp->wq_cp,
+			    &sp->scp[(cps[i])->protoidx].work_open);
+			}
 	}
 
 	/* notify low-level driver of state change */
 	sppp_notify_chg_wlocked(sp);
-
-	if (sp->pp_phase == SPPP_PHASE_NETWORK)
-		/* if no NCP is starting, close down */
-		sppp_lcp_check_and_close(sp);
 }
 
 static void
 sppp_lcp_tld(struct sppp *sp)
 {
-	int i;
-	uint32_t mask;
+	int i, pi, phase;
 
 	KASSERT(SPPP_WLOCKED(sp));
 
+	phase = sp->pp_phase;
+
 	sppp_change_phase(sp, SPPP_PHASE_TERMINATE);
 
 	/*
@@ -3078,10 +3134,16 @@ sppp_lcp_tld(struct sppp *sp)
 	 * ``a flurry of terminate-request packets'', as the RFC
 	 * describes it.
 	 */
-	for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) {
-		if ((sp->lcp.protos & mask) && ((cps[i])->flags & CP_LCP) == 0) {
-			(cps[i])->Down(sp, __UNCONST(&cps[i]));
-			(cps[i])->Close(sp,__UNCONST(&cps[i]));
+	for (i = 0; i < IDX_COUNT; i++) {
+		pi = (cps[i])->protoidx;
+		if (((cps[i])->flags & CP_LCP) == 0) {
+			/* skip if ncp was not started */
+			if (phase != SPPP_PHASE_NETWORK &&
+			    ((cps[i])->flags & CP_NCP) != 0)
+				continue;
+
+			sppp_wq_add(sp->wq_cp, &sp->scp[pi].work_down);
+			sppp_wq_add(sp->wq_cp, &sp->scp[pi].work_close);
 		}
 	}
 }
@@ -3092,7 +3154,6 @@ sppp_lcp_tls(struct sppp *sp)
 
 	KASSERT(SPPP_WLOCKED(sp));
 
-
 	if (sp->pp_max_auth_fail != 0 && sp->pp_auth_failures >= sp->pp_max_auth_fail) {
 		printf("%s: authentication failed %d times, not retrying again\n",
 		sp->pp_if.if_xname, sp->pp_auth_failures);
@@ -3176,6 +3237,17 @@ sppp_ncp_check(struct sppp *sp)
 	return 0;
 }
 
+static int
+sppp_auth_check(struct sppp *sp)
+{
+	int i, mask;
+
+	for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1)
+		if ((sp->lcp.protos & mask) && (cps[i])->flags & CP_AUTH)
+			return 1;
+	return 0;
+}
+
 /*
  * Re-check the open NCPs and see if we should terminate the link.
  * Called by the NCPs during their tlf action handling.
@@ -3186,15 +3258,38 @@ sppp_lcp_check_and_close(struct sppp *sp
 
 	KASSERT(SPPP_WLOCKED(sp));
 
-	if (sp->pp_phase < SPPP_PHASE_NETWORK)
+	if (sp->pp_phase < SPPP_PHASE_AUTHENTICATE) {
 		/* don't bother, we are already going down */
 		return;
+	}
 
-	if (sppp_ncp_check(sp))
+	if (sp->pp_phase == SPPP_PHASE_AUTHENTICATE &&
+	    sppp_auth_check(sp))
 		return;
+
+	if (sp->pp_phase >= SPPP_PHASE_NETWORK &&
+	    sppp_ncp_check(sp))
+		return;
+
 	sppp_wq_add(sp->wq_cp, &sp->scp[IDX_LCP].work_close);
 }
 
+static void
+sppp_lcp_uls(const struct cp *cp, struct sppp *sp)
+{
+
+	sp->lcp.protos |= (1 << cp->protoidx);
+	if (sp->scp[IDX_LCP].state == STATE_OPENED)
+		sppp_wq_add(sp->wq_cp, &sp->scp[cp->protoidx].work_up);
+}
+
+static void
+sppp_lcp_ulf(const struct cp *cp, struct sppp *sp)
+{
+
+	sp->lcp.protos &= ~(1 << cp->protoidx);
+	sppp_lcp_check_and_close(sp);
+}
 
 /*
  *--------------------------------------------------------------------------*
@@ -3718,8 +3813,7 @@ sppp_ipcp_tls(struct sppp *sp)
 {
 
 	KASSERT(SPPP_WLOCKED(sp));
-	/* indicate to LCP that it must stay alive */
-	sp->lcp.protos |= (1 << IDX_IPCP);
+	sppp_lcp_uls(&ipcp, sp);
 }
 
 static void
@@ -3727,9 +3821,7 @@ sppp_ipcp_tlf(struct sppp *sp)
 {
 
 	KASSERT(SPPP_WLOCKED(sp));
-
-	/* we no longer need LCP */
-	sp->lcp.protos &= ~(1 << IDX_IPCP);
+	sppp_lcp_ulf(&ipcp, sp);
 }
 
 static void
@@ -4288,8 +4380,7 @@ sppp_ipv6cp_tls(struct sppp *sp)
 {
 
 	KASSERT(SPPP_WLOCKED(sp));
-	/* indicate to LCP that it must stay alive */
-	sp->lcp.protos |= (1 << IDX_IPV6CP);
+	sppp_lcp_uls(&ipv6cp, sp);
 }
 
 static void
@@ -4297,8 +4388,7 @@ sppp_ipv6cp_tlf(struct sppp *sp)
 {
 
 	KASSERT(SPPP_WLOCKED(sp));
-	/* we no longer need LCP */
-	sp->lcp.protos &= ~(1 << IDX_IPV6CP);
+	sppp_lcp_ulf(&ipv6cp, sp);
 }
 
 static void
@@ -4443,82 +4533,21 @@ sppp_ipv6cp_scr(struct sppp *sp)
  *                                                                          *
  *--------------------------------------------------------------------------*
  */
-
 /*
- * The authentication protocols don't employ a full-fledged state machine as
- * the control protocols do, since they do have Open and Close events, but
- * not Up and Down, nor are they explicitly terminated.  Also, use of the
- * authentication protocols may be different in both directions (this makes
- * sense, think of a machine that never accepts incoming calls but only
- * calls out, it doesn't require the called party to authenticate itself).
- *
- * Our state machine for the local authentication protocol (we are requesting
- * the peer to authenticate) looks like:
- *
- *						    RCA-
- *	      +--------------------------------------------+
- *	      V					    scn,tld|
- *	  +--------+			       Close   +---------+ RCA+
- *	  |	   |<----------------------------------|	 |------+
- *   +--->| Closed |				TO*    | Opened	 | sca	|
- *   |	  |	   |-----+		       +-------|	 |<-----+
- *   |	  +--------+ irc |		       |       +---------+
- *   |	    ^		 |		       |	   ^
- *   |	    |		 |		       |	   |
- *   |	    |		 |		       |	   |
- *   |	 TO-|		 |		       |	   |
- *   |	    |tld  TO+	 V		       |	   |
- *   |	    |	+------->+		       |	   |
- *   |	    |	|	 |		       |	   |
- *   |	  +--------+	 V		       |	   |
- *   |	  |	   |<----+<--------------------+	   |
- *   |	  | Req-   | scr				   |
- *   |	  | Sent   |					   |
- *   |	  |	   |					   |
- *   |	  +--------+					   |
- *   | RCA- |	| RCA+					   |
- *   +------+	+------------------------------------------+
- *   scn,tld	  sca,irc,ict,tlu
- *
- *
- *   with:
- *
- *	Open:	LCP reached authentication phase
- *	Close:	LCP reached terminate phase
- *
- *	RCA+:	received reply (pap-req, chap-response), acceptable
- *	RCN:	received reply (pap-req, chap-response), not acceptable
- *	TO+:	timeout with restart counter >= 0
- *	TO-:	timeout with restart counter < 0
- *	TO*:	reschedule timeout for CHAP
- *
- *	scr:	send request packet (none for PAP, chap-challenge)
- *	sca:	send ack packet (pap-ack, chap-success)
- *	scn:	send nak packet (pap-nak, chap-failure)
- *	ict:	initialize re-challenge timer (CHAP only)
+ * The authentication protocols is implemented on the state machine for
+ * control protocols. And it uses following actions and events.
  *
- *	tlu:	this-layer-up, LCP reaches network phase
- *	tld:	this-layer-down, LCP enters terminate phase
- *
- * Note that in CHAP mode, after sending a new challenge, while the state
- * automaton falls back into Req-Sent state, it doesn't signal a tld
- * event to LCP, so LCP remains in network phase.  Only after not getting
- * any response (or after getting an unacceptable response), CHAP closes,
- * causing LCP to enter terminate phase.
- *
- * With PAP, there is no initial request that can be sent.  The peer is
- * expected to send one based on the successful negotiation of PAP as
- * the authentication protocol during the LCP option negotiation.
- *
- * Incoming authentication protocol requests (remote requests
- * authentication, we are peer) don't employ a state machine at all,
- * they are simply answered.  Some peers [Ascend P50 firmware rev
- * 4.50] react allergically when sending IPCP/IPv6CP requests while they are
- * still in authentication phase (thereby violating the standard that
- * demands that these NCP packets are to be discarded), so we keep
- * track of the peer demanding us to authenticate, and only proceed to
- * phase network once we've seen a positive acknowledge for the
- * authentication.
+ * Actions:
+ *    - scr: send CHAP_CHALLENGE and CHAP_RESPONSE
+ *    - sca: send CHAP_SUCCESS
+ *    - scn: send CHAP_FAILURE and shutdown lcp
+ * Events:
+ *    - RCR+: receive CHAP_RESPONSE containing correct digest
+ *    - RCR-: receive CHAP_RESPONSE containing wrong digest
+ *    - RCA: receive CHAP_SUCCESS
+ *    - RCN: (this event is unused)
+ *    - TO+: re-send CHAP_CHALLENGE and CHAP_RESPONSE
+ *    - TO-: this layer finish
  */
 
 /*
@@ -4530,7 +4559,7 @@ sppp_chap_input(struct sppp *sp, struct 
 	STDDCL;
 	struct lcp_header *h;
 	int len, x;
-	u_char *value, *name, digest[sizeof(sp->myauth.challenge)], dsize;
+	u_char *value, *name, digest[sizeof(sp->chap.challenge)];
 	int value_len, name_len;
 	MD5_CTX ctx;
 
@@ -4595,15 +4624,11 @@ sppp_chap_input(struct sppp *sp, struct 
 		MD5Update(&ctx, &h->ident, 1);
 		MD5Update(&ctx, sp->myauth.secret, sp->myauth.secret_len);
 		MD5Update(&ctx, value, value_len);
-		MD5Final(digest, &ctx);
-		dsize = sizeof digest;
+		MD5Final(sp->chap.digest, &ctx);
+		sp->chap.digest_len = sizeof(sp->chap.digest);
+		sp->scp[IDX_CHAP].rconfid = h->ident;
 
-		sppp_auth_send(&chap, sp, CHAP_RESPONSE, h->ident,
-			       sizeof dsize, (const char *)&dsize,
-			       sizeof digest, digest,
-			       sp->myauth.name_len,
-			       sp->myauth.name,
-			       0);
+		sppp_wq_add(sp->wq_cp, &sp->chap.work_challenge_rcvd);
 		break;
 
 	case CHAP_SUCCESS:
@@ -4616,25 +4641,66 @@ sppp_chap_input(struct sppp *sp, struct 
 			}
 			addlog("\n");
 		}
+
+		if (h->ident != sp->scp[IDX_CHAP].rconfid) {
+			if (debug) {
+				log(LOG_DEBUG, "%s: %s id mismatch 0x%x != 0x%x\n",
+				       ifp->if_xname, chap.name,
+				       h->ident, sp->scp[IDX_CHAP].rconfid);
+			}
+			if_statinc(ifp, if_ierrors);
+			break;
+		}
+
+		if (sp->chap.digest_len == 0) {
+			if (debug) {
+				log(LOG_DEBUG,
+				    "%s: receive CHAP success without challenge\n",
+				    ifp->if_xname);
+			}
+			if_statinc(ifp, if_ierrors);
+			break;
+		}
+
 		x = splnet();
 		sp->pp_auth_failures = 0;
 		sp->pp_flags &= ~PP_NEEDAUTH;
-		if (sp->myauth.proto == PPP_CHAP &&
-		    (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) &&
-		    (sp->lcp.protos & (1 << IDX_CHAP)) == 0) {
+		splx(x);
+		memset(sp->chap.digest, 0, sizeof(sp->chap.digest));
+		sp->chap.digest_len = 0;
+
+		if (!ISSET(sppp_auth_role(&chap, sp), SPPP_AUTH_SERV)) {
 			/*
-			 * We are authenticator for CHAP but didn't
-			 * complete yet.  Leave it to tlu to proceed
-			 * to network phase.
+			 * we are not authenticator for CHAP,
+			 * generate a dummy RCR+ event without CHAP_RESPONSE
 			 */
-			splx(x);
-			break;
+			sp->scp[IDX_CHAP].rcr_type = CONF_ACK;
+			sppp_wq_add(sp->wq_cp, &sp->scp[IDX_CHAP].work_rcr);
 		}
-		splx(x);
-		sppp_phase_network(sp);
+		sppp_wq_add(sp->wq_cp, &sp->scp[IDX_CHAP].work_rca);
 		break;
 
 	case CHAP_FAILURE:
+		if (h->ident != sp->scp[IDX_CHAP].rconfid) {
+			if (debug) {
+				log(LOG_DEBUG, "%s: %s id mismatch 0x%x != 0x%x\n",
+				       ifp->if_xname, chap.name,
+				       h->ident, sp->scp[IDX_CHAP].rconfid);
+			}
+			if_statinc(ifp, if_ierrors);
+			break;
+		}
+
+		if (sp->chap.digest_len == 0) {
+			if (debug) {
+				log(LOG_DEBUG,
+				    "%s: receive CHAP failure without challenge\n",
+				    ifp->if_xname);
+			}
+			if_statinc(ifp, if_ierrors);
+			break;
+		}
+
 		x = splnet();
 		sp->pp_auth_failures++;
 		splx(x);
@@ -4649,14 +4715,21 @@ sppp_chap_input(struct sppp *sp, struct 
 		} else
 			log(LOG_INFO, "%s: chap failure\n",
 			    ifp->if_xname);
-		/* await LCP shutdown by authenticator */
+
+		memset(sp->chap.digest, 0, sizeof(sp->chap.digest));
+		sp->chap.digest_len = 0;
+		/*
+		 * await LCP shutdown by authenticator,
+		 * so we don't have to enqueue sc->scp[IDX_CHAP].work_rcn
+		 */
 		break;
 
 	/* response is my authproto */
 	case CHAP_RESPONSE:
-		if (sp->hisauth.secret == NULL) {
+		if (sp->hisauth.name == NULL || sp->hisauth.secret == NULL) {
 			/* can't do anything useful */
-			printf("%s: chap input without his secret being set\n",
+			printf("%s: chap response"
+			    " without his name and his secret being set\n",
 			    ifp->if_xname);
 		    break;
 		}
@@ -4687,7 +4760,10 @@ sppp_chap_input(struct sppp *sp, struct 
 				    ifp->if_xname,
 				    h->ident, sp->scp[IDX_CHAP].confid);
 			break;
+		} else {
+			sp->scp[IDX_CHAP].rconfid = h->ident;
 		}
+
 		if (sp->hisauth.name != NULL &&
 		    (name_len != sp->hisauth.name_len
 		    || memcmp(name, sp->hisauth.name, name_len) != 0)) {
@@ -4698,8 +4774,13 @@ sppp_chap_input(struct sppp *sp, struct 
 			sppp_print_string(sp->hisauth.name,
 					  sp->hisauth.name_len);
 			addlog("\n");
-		    goto chap_failure;
+
+			/* generate RCR- event */
+			sp->scp[IDX_CHAP].rcr_type = CONF_NAK;
+			sppp_wq_add(sp->wq_cp, &sp->scp[IDX_CHAP].work_rcr);
+			break;
 		}
+
 		if (debug) {
 			log(LOG_DEBUG, "%s: chap input(%s) "
 			    "<%s id=0x%x len=%d name=",
@@ -4712,48 +4793,37 @@ sppp_chap_input(struct sppp *sp, struct 
 			sppp_print_bytes(value, value_len);
 			addlog(">\n");
 		}
-		if (value_len != sizeof(sp->hisauth.challenge)) {
+
+		if (value_len == sizeof(sp->chap.challenge) &&
+		    value_len == sizeof(sp->chap.digest)) {
+			MD5Init(&ctx);
+			MD5Update(&ctx, &h->ident, 1);
+			MD5Update(&ctx, sp->hisauth.secret, sp->hisauth.secret_len);
+			MD5Update(&ctx, sp->chap.challenge, sizeof(sp->chap.challenge));
+			MD5Final(digest, &ctx);
+
+			if (memcmp(digest, value, value_len) == 0) {
+				sp->scp[IDX_CHAP].rcr_type = CONF_ACK;
+				if (!ISSET(sppp_auth_role(&chap, sp), SPPP_AUTH_PEER) ||
+				    sp->chap.rechallenging) {
+					/* generate a dummy RCA event*/
+					sppp_wq_add(sp->wq_cp, &sp->scp[IDX_CHAP].work_rca);
+				}
+			} else {
+				sp->scp[IDX_CHAP].rcr_type = CONF_NAK;
+			}
+		} else {
 			if (debug)
 				log(LOG_DEBUG,
 				    "%s: chap bad hash value length: "
 				    "%d bytes, should be %zu\n",
 				    ifp->if_xname, value_len,
-				    sizeof(sp->hisauth.challenge));
-			goto chap_failure;
-		}
-
-		MD5Init(&ctx);
-		MD5Update(&ctx, &h->ident, 1);
-		MD5Update(&ctx, sp->hisauth.secret, sp->hisauth.secret_len);
-		MD5Update(&ctx, sp->hisauth.challenge, sizeof(sp->hisauth.challenge));
-		MD5Final(digest, &ctx);
-
-#define FAILMSG "Failed..."
-#define SUCCMSG "Welcome!"
+				    sizeof(sp->chap.challenge));
 
-		if (value_len != sizeof digest ||
-		    memcmp(digest, value, value_len) != 0) {
-chap_failure:
-			KASSERT(SPPP_WLOCKED(sp));
-			/* action scn, tld */
-			sp->pp_auth_failures++;
-			sppp_auth_send(&chap, sp, CHAP_FAILURE, h->ident,
-				       sizeof(FAILMSG) - 1, (const u_char *)FAILMSG,
-				       0);
-			chap.tld(sp);
-			break;
-		}
-		sp->pp_auth_failures = 0;
-		/* action sca, perhaps tlu */
-		if (sp->scp[IDX_CHAP].state == STATE_REQ_SENT ||
-		    sp->scp[IDX_CHAP].state == STATE_OPENED)
-			sppp_auth_send(&chap, sp, CHAP_SUCCESS, h->ident,
-				       sizeof(SUCCMSG) - 1, (const u_char *)SUCCMSG,
-				       0);
-		if (sp->scp[IDX_CHAP].state == STATE_REQ_SENT) {
-			sppp_cp_change_state(&chap, sp, STATE_OPENED);
-			chap.tlu(sp);
+			sp->scp[IDX_CHAP].rcr_type = CONF_NAK;
 		}
+
+		sppp_wq_add(sp->wq_cp, &sp->scp[IDX_CHAP].work_rcr);
 		break;
 
 	default:
@@ -4781,81 +4851,58 @@ sppp_chap_init(struct sppp *sp)
 
 	KASSERT(SPPP_WLOCKED(sp));
 
-	/* Chap doesn't have STATE_INITIAL at all. */
-	sp->scp[IDX_CHAP].state = STATE_CLOSED;
+	sp->scp[IDX_CHAP].state = STATE_INITIAL;
 	sp->scp[IDX_CHAP].fail_counter = 0;
 	sp->scp[IDX_CHAP].seq = 0;
 	sp->scp[IDX_CHAP].rseq = 0;
-	callout_init(&sp->scp[IDX_CHAP].ch, CALLOUT_MPSAFE);
-	callout_setfunc(&sp->scp[IDX_CHAP].ch,
-	    sppp_chap_TO, sp);
+	SPPP_WQ_SET(&sp->chap.work_challenge_rcvd,
+	    sppp_chap_rcv_challenge_event, &chap);
+	sppp_cp_init(&chap, sp);
 }
 
 static void
-sppp_chap_open(struct sppp *sp, void *xcp __unused)
+sppp_chap_up(struct sppp *sp, void *xcp)
 {
 
 	KASSERT(SPPP_WLOCKED(sp));
-	if (sp->hisauth.proto == PPP_CHAP &&
-	    (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0) {
-		/* we are authenticator for CHAP, start it */
-		chap.scr(sp);
-		sp->scp[IDX_CHAP].rst_counter = sp->lcp.max_configure;
-		sppp_cp_change_state(&chap, sp, STATE_REQ_SENT);
-	}
-	/* nothing to be done if we are peer, await a challenge */
+	sppp_up_event(sp, xcp);
 }
 
 static void
-sppp_chap_close(struct sppp *sp, void *xcp __unused)
+sppp_chap_down(struct sppp *sp, void *xcp)
 {
 
 	KASSERT(SPPP_WLOCKED(sp));
-	if (sp->scp[IDX_CHAP].state != STATE_CLOSED)
-		sppp_cp_change_state(&chap, sp, STATE_CLOSED);
+	sppp_down_event(sp, xcp);
 }
 
 static void
-sppp_chap_TO(void *cookie)
+sppp_chap_open(struct sppp *sp, void *xcp)
 {
-	struct sppp *sp = (struct sppp *)cookie;
-	STDDCL;
-	int s;
 
-	s = splnet();
+	KASSERT(SPPP_WLOCKED(sp));
 
-	SPPP_LOCK(sp, RW_WRITER);
+	memset(sp->chap.digest, 0, sizeof(sp->chap.digest));
+	sp->chap.digest_len = 0;
+	sp->chap.rechallenging = false;
+	sp->chap.response_rcvd = false;
+	sppp_open_event(sp, xcp);
+}
 
-	if (debug)
-		log(LOG_DEBUG, "%s: chap TO(%s) rst_counter = %d\n",
-		    ifp->if_xname,
-		    sppp_state_name(sp->scp[IDX_CHAP].state),
-		    sp->scp[IDX_CHAP].rst_counter);
+static void
+sppp_chap_close(struct sppp *sp, void *xcp)
+{
 
-	if (--sp->scp[IDX_CHAP].rst_counter < 0)
-		/* TO- event */
-		switch (sp->scp[IDX_CHAP].state) {
-		case STATE_REQ_SENT:
-			chap.tld(sp);
-			sppp_cp_change_state(&chap, sp, STATE_CLOSED);
-			break;
-		}
-	else
-		/* TO+ (or TO*) event */
-		switch (sp->scp[IDX_CHAP].state) {
-		case STATE_OPENED:
-			/* TO* event */
-			sp->scp[IDX_CHAP].rst_counter = sp->lcp.max_configure;
-			/* fall through */
-		case STATE_REQ_SENT:
-			chap.scr(sp);
-			/* sppp_cp_change_state() will restart the timer */
-			sppp_cp_change_state(&chap, sp, STATE_REQ_SENT);
-			break;
-		}
+	KASSERT(SPPP_WLOCKED(sp));
+	sppp_close_event(sp, xcp);
+}
 
-	SPPP_UNLOCK(sp);
-	splx(s);
+static void
+sppp_chap_TO(struct sppp *sp, void *xcp)
+{
+
+	KASSERT(SPPP_WLOCKED(sp));
+	sppp_auth_to_event(xcp, sp);
 }
 
 static void
@@ -4868,6 +4915,12 @@ sppp_chap_tlu(struct sppp *sp)
 
 	i = 0;
 	sp->scp[IDX_CHAP].rst_counter = sp->lcp.max_configure;
+	x = splnet();
+	sp->pp_auth_failures = 0;
+	splx(x);
+
+	log(LOG_DEBUG, "%s: chap %s", ifp->if_xname,
+	    sp->pp_phase == SPPP_PHASE_NETWORK ? "reconfirmed" : "tlu");
 
 	/*
 	 * Some broken CHAP implementations (Conware CoNet, firmware
@@ -4875,42 +4928,21 @@ sppp_chap_tlu(struct sppp *sp)
 	 * initial challenge-response exchange has taken place.
 	 * Provide for an option to avoid rechallenges.
 	 */
-	if ((sp->hisauth.flags & SPPP_AUTHFLAG_NORECHALLENGE) == 0) {
+	if (ISSET(sppp_auth_role(&chap, sp), SPPP_AUTH_SERV) &&
+	    (sp->hisauth.flags & SPPP_AUTHFLAG_NORECHALLENGE) == 0) {
 		/*
 		 * Compute the re-challenge timeout.  This will yield
 		 * a number between 300 and 810 seconds.
 		 */
 		i = 300 + ((unsigned)(cprng_fast32() & 0xff00) >> 7);
-
 		callout_schedule(&sp->scp[IDX_CHAP].ch, i * hz);
-	}
 
-	if (debug) {
-		log(LOG_DEBUG,
-		    "%s: chap %s, ",
-		    ifp->if_xname,
-		    sp->pp_phase == SPPP_PHASE_NETWORK? "reconfirmed": "tlu");
-		if ((sp->hisauth.flags & SPPP_AUTHFLAG_NORECHALLENGE) == 0)
-			addlog("next re-challenge in %d seconds\n", i);
-		else
-			addlog("re-challenging suppressed\n");
+		if (debug) {
+			addlog(", next rechallenge in %d seconds", i);
+		}
 	}
 
-	x = splnet();
-	sp->pp_auth_failures = 0;
-	/* indicate to LCP that we need to be closed down */
-	sp->lcp.protos |= (1 << IDX_CHAP);
-
-	if (sp->pp_flags & PP_NEEDAUTH) {
-		/*
-		 * Remote is authenticator, but his auth proto didn't
-		 * complete yet.  Defer the transition to network
-		 * phase.
-		 */
-		splx(x);
-		return;
-	}
-	splx(x);
+	addlog("\n");
 
 	/*
 	 * If we are already in phase network, we are done here.  This
@@ -4921,45 +4953,114 @@ sppp_chap_tlu(struct sppp *sp)
 }
 
 static void
-sppp_chap_tld(struct sppp *sp)
+sppp_chap_tls(struct sppp *sp)
+{
+
+	KASSERT(SPPP_WLOCKED(sp));
+	sppp_lcp_uls(&chap, sp);
+}
+
+static void
+sppp_chap_tlf(struct sppp *sp)
 {
 	STDDCL;
 
 	KASSERT(SPPP_WLOCKED(sp));
 
 	if (debug)
-		log(LOG_DEBUG, "%s: chap tld\n", ifp->if_xname);
-	callout_stop(&sp->scp[IDX_CHAP].ch);
-	sp->lcp.protos &= ~(1 << IDX_CHAP);
+		log(LOG_DEBUG, "%s: chap tlf\n", ifp->if_xname);
 
-	sppp_wq_add(sp->wq_cp, &sp->scp[IDX_LCP].work_close);
+	sppp_lcp_ulf(&chap, sp);
 }
 
 static void
 sppp_chap_scr(struct sppp *sp)
 {
 	uint32_t *ch;
-	u_char clen = 4 * sizeof(uint32_t);
+	u_char clen, dsize;
+	int role;
 
 	KASSERT(SPPP_WLOCKED(sp));
 
-	if (sp->hisauth.name == NULL) {
-	    /* can't do anything useful */
-	    printf("%s: chap starting without his name being set\n",
-	    	sp->pp_if.if_xname);
-	    return;
+	role = sppp_auth_role(&chap, sp);
+
+	if (ISSET(role, SPPP_AUTH_SERV) &&
+	    !sp->chap.response_rcvd) {
+		/* we are authenticator for CHAP, send challenge */
+		ch = (uint32_t *)sp->chap.challenge;
+		clen = sizeof(sp->chap.challenge);
+		/* Compute random challenge. */
+		cprng_strong(kern_cprng, ch, clen, 0);
+
+		sp->scp[IDX_CHAP].confid = ++sp->scp[IDX_CHAP].seq;
+		sppp_auth_send(&chap, sp, CHAP_CHALLENGE, sp->scp[IDX_CHAP].confid,
+		    sizeof(clen), (const char *)&clen,
+		    sizeof(sp->chap.challenge), sp->chap.challenge,
+		    0);
 	}
 
-	/* Compute random challenge. */
-	ch = (uint32_t *)sp->hisauth.challenge;
-	cprng_strong(kern_cprng, ch, clen, 0);
+	if (ISSET(role, SPPP_AUTH_PEER) &&
+	    sp->chap.digest_len > 0) {
+		/* we are peer for CHAP, send response */
+		dsize = sp->chap.digest_len;
 
-	sp->scp[IDX_CHAP].confid = ++sp->scp[IDX_CHAP].seq;
+		sppp_auth_send(&chap, sp, CHAP_RESPONSE, sp->scp[IDX_CHAP].rconfid,
+		    sizeof(dsize), (const char *)&dsize,
+		    sp->chap.digest_len, sp->chap.digest,
+		    sp->myauth.name_len, sp->myauth.name, 0);
+	}
+}
+
+static void
+sppp_chap_scan(const struct cp *cp __unused, struct sppp *sp)
+{
+	u_char type, rconfid, mlen;
+	const char *msg;
 
-	sppp_auth_send(&chap, sp, CHAP_CHALLENGE, sp->scp[IDX_CHAP].confid,
-		       sizeof clen, (const char *)&clen,
-		       sizeof(sp->hisauth.challenge), sp->hisauth.challenge,
-		       0);
+	if (!ISSET(sppp_auth_role(&chap, sp), SPPP_AUTH_SERV))
+		return;
+
+	rconfid = sp->scp[IDX_CHAP].rconfid;
+	if (sp->scp[IDX_CHAP].rcr_type == CONF_ACK) {
+		type = CHAP_SUCCESS;
+		msg = SUCCMSG;
+		mlen = sizeof(SUCCMSG) - 1;
+	} else {
+		type = CHAP_FAILURE;
+		msg = FAILMSG;
+		mlen = sizeof(FAILMSG) - 1;
+	}
+
+	sppp_auth_send(&chap, sp, type, rconfid,
+	   mlen, (const u_char *)msg, 0);
+	sp->chap.response_rcvd = true;
+
+	if (type == CHAP_SUCCESS) {
+		sp->pp_auth_failures = 0;
+	} else {
+		sp->pp_auth_failures++;
+		/* shutdown LCP if auth failed */
+		sppp_wq_add(sp->wq_cp, &sp->scp[IDX_LCP].work_close);
+	}
+}
+
+static void
+sppp_chap_rcv_challenge_event(struct sppp *sp, void *xcp)
+{
+	const struct cp *cp = xcp;
+
+	sp->chap.rechallenging = false;
+
+	switch (sp->scp[IDX_CHAP].state) {
+	case STATE_REQ_SENT:
+		sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
+		cp->scr(sp);
+		break;
+	case STATE_OPENED:
+		sppp_cp_change_state(cp, sp, STATE_ACK_SENT);
+		cp->scr(sp);
+		break;
+	}
 }
 
 /*
@@ -4970,10 +5071,18 @@ sppp_chap_scr(struct sppp *sp)
  *--------------------------------------------------------------------------*
  */
 /*
- * For PAP, we need to keep a little state also if we are the peer, not the
- * authenticator.  This is since we don't get a request to authenticate, but
- * have to repeatedly authenticate ourself until we got a response (or the
- * retry counter is expired).
+ * PAP uses following actions and events.
+ * Actions:
+ *    - scr: send PAP_REQ
+ *    - sca: send PAP_ACK
+ *    - scn: send PAP_NAK
+ * Events:
+ *    - RCR+: receive PAP_REQ containing correct username and password
+ *    - RCR-: receive PAP_REQ containing wrong username and password
+ *    - RCA: receive PAP_ACK
+ *    - RCN: (this event is unused)
+ *    - TO+: re-send PAP_REQ
+ *    - TO-: this layer finish
  */
 
 /*
@@ -4984,7 +5093,6 @@ sppp_pap_input(struct sppp *sp, struct m
 	STDDCL;
 	struct lcp_header *h;
 	int len, x;
-	u_char mlen;
 	char *name, *secret;
 	int name_len, secret_len;
 
@@ -5013,8 +5121,8 @@ sppp_pap_input(struct sppp *sp, struct m
 	case PAP_REQ:
 		if (sp->hisauth.name == NULL || sp->hisauth.secret == NULL) {
 			/* can't do anything useful */
-			printf("%s: "
-			    "pap request without his name and his secret being set\n",
+			printf("%s: pap request"
+			    " without his name and his secret being set\n",
 			    ifp->if_xname);
 			break;
 		}
@@ -5048,38 +5156,27 @@ sppp_pap_input(struct sppp *sp, struct m
 			sppp_print_string((char *)secret, secret_len);
 			addlog(">\n");
 		}
-		if (name_len != sp->hisauth.name_len ||
-		    secret_len != sp->hisauth.secret_len ||
-		    memcmp(name, sp->hisauth.name, name_len) != 0 ||
-		    memcmp(secret, sp->hisauth.secret, secret_len) != 0) {
-			/* action scn, tld */
-			sp->pp_auth_failures++;
-			mlen = sizeof(FAILMSG) - 1;
-			sppp_auth_send(&pap, sp, PAP_NAK, h->ident,
-				       sizeof mlen, (const char *)&mlen,
-				       sizeof(FAILMSG) - 1, (const u_char *)FAILMSG,
-				       0);
-			pap.tld(sp);
-			break;
-		}
-		/* action sca, perhaps tlu */
-		if (sp->scp[IDX_PAP].state == STATE_REQ_SENT ||
-		    sp->scp[IDX_PAP].state == STATE_OPENED) {
-			mlen = sizeof(SUCCMSG) - 1;
-			sppp_auth_send(&pap, sp, PAP_ACK, h->ident,
-				       sizeof mlen, (const char *)&mlen,
-				       sizeof(SUCCMSG) - 1, (const u_char *)SUCCMSG,
-				       0);
-		}
-		if (sp->scp[IDX_PAP].state == STATE_REQ_SENT) {
-			sppp_cp_change_state(&pap, sp, STATE_OPENED);
-			pap.tlu(sp);
+
+		sp->scp[IDX_PAP].rconfid = h->ident;
+
+		if (name_len == sp->hisauth.name_len &&
+		    memcmp(name, sp->hisauth.name, name_len) == 0 &&
+		    secret_len == sp->hisauth.secret_len &&
+		    memcmp(secret, sp->hisauth.secret, secret_len) == 0) {
+			sp->scp[IDX_PAP].rcr_type = CONF_ACK;
+			if (!ISSET(sppp_auth_role(&pap, sp), SPPP_AUTH_PEER)) {
+				/* generate a dummy RCA event*/
+				sppp_wq_add(sp->wq_cp, &sp->scp[IDX_PAP].work_rca);
+			}
+		} else {
+			sp->scp[IDX_PAP].rcr_type = CONF_NAK;
 		}
+
+		sppp_wq_add(sp->wq_cp, &sp->scp[IDX_PAP].work_rcr);
 		break;
 
 	/* ack and nak are his authproto */
 	case PAP_ACK:
-		callout_stop(&sp->pap_my_to_ch);
 		if (debug) {
 			log(LOG_DEBUG, "%s: pap success",
 			    ifp->if_xname);
@@ -5091,27 +5188,32 @@ sppp_pap_input(struct sppp *sp, struct m
 			}
 			addlog("\n");
 		}
+
+		if (h->ident != sp->scp[IDX_PAP].confid) {
+			if (debug) {
+				log(LOG_DEBUG, "%s: %s id mismatch 0x%x != 0x%x\n",
+				       ifp->if_xname, pap.name,
+				       h->ident, sp->scp[IDX_PAP].rconfid);
+			}
+			if_statinc(ifp, if_ierrors);
+			break;
+		}
+
 		x = splnet();
 		sp->pp_auth_failures = 0;
 		sp->pp_flags &= ~PP_NEEDAUTH;
-		if (sp->myauth.proto == PPP_PAP &&
-		    (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) &&
-		    (sp->lcp.protos & (1 << IDX_PAP)) == 0) {
-			/*
-			 * We are authenticator for PAP but didn't
-			 * complete yet.  Leave it to tlu to proceed
-			 * to network phase.
-			 */
-			splx(x);
-			break;
-		}
 		splx(x);
-		sppp_phase_network(sp);
+
+		/* we are not authenticator, generate a dummy RCR+ event */
+		if (!ISSET(sppp_auth_role(&pap, sp), SPPP_AUTH_SERV)) {
+			sp->scp[IDX_PAP].rcr_type = CONF_ACK;
+			sppp_wq_add(sp->wq_cp, &sp->scp[IDX_PAP].work_rcr);
+		}
+
+		sppp_wq_add(sp->wq_cp, &sp->scp[IDX_PAP].work_rca);
 		break;
 
 	case PAP_NAK:
-		callout_stop(&sp->pap_my_to_ch);
-		sp->pp_auth_failures++;
 		if (debug) {
 			log(LOG_INFO, "%s: pap failure",
 			    ifp->if_xname);
@@ -5125,7 +5227,22 @@ sppp_pap_input(struct sppp *sp, struct m
 		} else
 			log(LOG_INFO, "%s: pap failure\n",
 			    ifp->if_xname);
-		/* await LCP shutdown by authenticator */
+
+		if (h->ident != sp->scp[IDX_PAP].confid) {
+			if (debug) {
+				log(LOG_DEBUG, "%s: %s id mismatch 0x%x != 0x%x\n",
+				       ifp->if_xname, pap.name,
+				       h->ident, sp->scp[IDX_PAP].rconfid);
+			}
+			if_statinc(ifp, if_ierrors);
+			break;
+		}
+
+		sp->pp_auth_failures++;
+		/*
+		 * await LCP shutdown by authenticator,
+		 * so we don't have to enqueue sc->scp[IDX_PAP].work_rcn
+		 */
 		break;
 
 	default:
@@ -5151,107 +5268,63 @@ sppp_pap_init(struct sppp *sp)
 
 	KASSERT(SPPP_WLOCKED(sp));
 
-	/* PAP doesn't have STATE_INITIAL at all. */
-	sp->scp[IDX_PAP].state = STATE_CLOSED;
+	sp->scp[IDX_PAP].state = STATE_INITIAL;
 	sp->scp[IDX_PAP].fail_counter = 0;
 	sp->scp[IDX_PAP].seq = 0;
 	sp->scp[IDX_PAP].rseq = 0;
-	callout_init(&sp->scp[IDX_PAP].ch, CALLOUT_MPSAFE);
-	callout_setfunc(&sp->scp[IDX_PAP].ch,
-	    sppp_pap_TO, sp);
-	callout_init(&sp->pap_my_to_ch, CALLOUT_MPSAFE);
+	sppp_cp_init(&pap, sp);
 }
 
 static void
-sppp_pap_open(struct sppp *sp, void *xcp __unused)
+sppp_pap_up(struct sppp *sp, void *xcp)
 {
 
 	KASSERT(SPPP_WLOCKED(sp));
-
-	if (sp->hisauth.proto == PPP_PAP &&
-	    (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0) {
-		/* we are authenticator for PAP, start our timer */
-		sp->scp[IDX_PAP].rst_counter = sp->lcp.max_configure;
-		sppp_cp_change_state(&pap, sp, STATE_REQ_SENT);
-	}
-	if (sp->myauth.proto == PPP_PAP) {
-		/* we are peer, send a request, and start a timer */
-		pap.scr(sp);
-		callout_reset(&sp->pap_my_to_ch, sp->lcp.timeout,
-		    sppp_pap_my_TO, sp);
-	}
+	sppp_up_event(sp, xcp);
 }
 
 static void
-sppp_pap_close(struct sppp *sp, void *xcp __unused)
+sppp_pap_down(struct sppp *sp, void *xcp)
 {
 
 	KASSERT(SPPP_WLOCKED(sp));
-
-	if (sp->scp[IDX_PAP].state != STATE_CLOSED)
-		sppp_cp_change_state(&pap, sp, STATE_CLOSED);
+	sppp_down_event(sp, xcp);
 }
 
-/*
- * That's the timeout routine if we are authenticator.  Since the
- * authenticator is basically passive in PAP, we can't do much here.
- */
 static void
-sppp_pap_TO(void *cookie)
+sppp_pap_open(struct sppp *sp, void *xcp)
 {
-	struct sppp *sp = (struct sppp *)cookie;
-	STDDCL;
-	int s;
 
-	s = splnet();
-	SPPP_LOCK(sp, RW_WRITER);
+	KASSERT(SPPP_WLOCKED(sp));
+	sppp_open_event(sp, xcp);
+}
 
-	if (debug)
-		log(LOG_DEBUG, "%s: pap TO(%s) rst_counter = %d\n",
-		    ifp->if_xname,
-		    sppp_state_name(sp->scp[IDX_PAP].state),
-		    sp->scp[IDX_PAP].rst_counter);
+static void
+sppp_pap_close(struct sppp *sp, void *xcp)
+{
 
-	if (--sp->scp[IDX_PAP].rst_counter < 0)
-		/* TO- event */
-		switch (sp->scp[IDX_PAP].state) {
-		case STATE_REQ_SENT:
-			pap.tld(sp);
-			sppp_cp_change_state(&pap, sp, STATE_CLOSED);
-			break;
-		}
-	else
-		/* TO+ event, not very much we could do */
-		switch (sp->scp[IDX_PAP].state) {
-		case STATE_REQ_SENT:
-			/* sppp_cp_change_state() will restart the timer */
-			sppp_cp_change_state(&pap, sp, STATE_REQ_SENT);
-			break;
-		}
+	KASSERT(SPPP_WLOCKED(sp));
+	sppp_close_event(sp, xcp);
+}
 
-	SPPP_UNLOCK(sp);
-	splx(s);
+static void
+sppp_pap_tls(struct sppp *sp)
+{
+
+	KASSERT(SPPP_WLOCKED(sp));
+	sppp_lcp_uls(&pap, sp);
 }
 
-/*
- * That's the timeout handler if we are peer.  Since the peer is active,
- * we need to retransmit our PAP request since it is apparently lost.
- * XXX We should impose a max counter.
- */
 static void
-sppp_pap_my_TO(void *cookie)
+sppp_pap_tlf(struct sppp *sp)
 {
-	struct sppp *sp = (struct sppp *)cookie;
 	STDDCL;
 
-	SPPP_LOCK(sp, RW_WRITER);
-	if (debug)
-		log(LOG_DEBUG, "%s: pap peer TO\n",
-		    ifp->if_xname);
-
-	pap.scr(sp);
+	KASSERT(SPPP_WLOCKED(sp));
 
-	SPPP_UNLOCK(sp);
+	if (debug)
+		log(LOG_DEBUG, "%s: pap tlf\n", ifp->if_xname);
+	sppp_lcp_ulf(&pap, sp);
 }
 
 static void
@@ -5260,8 +5333,6 @@ sppp_pap_tlu(struct sppp *sp)
 	STDDCL;
 	int x;
 
-	KASSERT(SPPP_WLOCKED(sp));
-
 	sp->scp[IDX_PAP].rst_counter = sp->lcp.max_configure;
 
 	if (debug)
@@ -5270,36 +5341,21 @@ sppp_pap_tlu(struct sppp *sp)
 
 	x = splnet();
 	sp->pp_auth_failures = 0;
-	/* indicate to LCP that we need to be closed down */
-	sp->lcp.protos |= (1 << IDX_PAP);
-
-	if (sp->pp_flags & PP_NEEDAUTH) {
-		/*
-		 * Remote is authenticator, but his auth proto didn't
-		 * complete yet.  Defer the transition to network
-		 * phase.
-		 */
-		splx(x);
-		return;
-	}
 	splx(x);
+
 	sppp_phase_network(sp);
 }
 
+/*
+ * That's the timeout routine if we are authenticator.  Since the
+ * authenticator is basically passive in PAP, we can't do much here.
+ */
 static void
-sppp_pap_tld(struct sppp *sp)
+sppp_pap_TO(struct sppp *sp, void *xcp)
 {
-	STDDCL;
 
 	KASSERT(SPPP_WLOCKED(sp));
-
-	if (debug)
-		log(LOG_DEBUG, "%s: pap tld\n", ifp->if_xname);
-	callout_stop(&sp->scp[IDX_PAP].ch);
-	callout_stop(&sp->pap_my_to_ch);
-	sp->lcp.protos &= ~(1 << IDX_PAP);
-
-	sppp_wq_add(sp->wq_cp, &sp->scp[IDX_LCP].work_close);
+	sppp_auth_to_event(xcp, sp);
 }
 
 static void
@@ -5309,23 +5365,57 @@ sppp_pap_scr(struct sppp *sp)
 
 	KASSERT(SPPP_WLOCKED(sp));
 
-	if (sp->myauth.secret == NULL || sp->myauth.name == NULL) {
-	    /* can't do anything useful */
-	    printf("%s: pap starting without my name and secret being set\n",
-	    	sp->pp_if.if_xname);
-	    return;
-	}
-
-	sp->scp[IDX_PAP].confid = ++sp->scp[IDX_PAP].seq;
-	pwdlen = sp->myauth.secret_len;
-	idlen = sp->myauth.name_len;
-
-	sppp_auth_send(&pap, sp, PAP_REQ, sp->scp[IDX_PAP].confid,
-		       sizeof idlen, (const char *)&idlen,
-		       idlen, sp->myauth.name,
-		       sizeof pwdlen, (const char *)&pwdlen,
-		       pwdlen, sp->myauth.secret,
-		       0);
+	if (ISSET(sppp_auth_role(&pap, sp), SPPP_AUTH_PEER) &&
+	    sp->scp[IDX_PAP].state != STATE_ACK_RCVD) {
+		if (sp->myauth.secret == NULL || sp->myauth.name == NULL) {
+			log(LOG_DEBUG, "%s: couldn't send PAP_REQ "
+			    "because of no name or no secret\n",
+			    sp->pp_if.if_xname);
+		} else {
+			sp->scp[IDX_PAP].confid = ++sp->scp[IDX_PAP].seq;
+			pwdlen = sp->myauth.secret_len;
+			idlen = sp->myauth.name_len;
+
+			sppp_auth_send(&pap, sp, PAP_REQ, sp->scp[IDX_PAP].confid,
+			    sizeof idlen, (const char *)&idlen,
+			    idlen, sp->myauth.name,
+			    sizeof pwdlen, (const char *)&pwdlen,
+			    pwdlen, sp->myauth.secret,
+			    0);
+		}
+	}
+}
+
+static void
+sppp_pap_scan(const struct cp *cp __unused, struct sppp *sp)
+{
+	u_char type, rconfid, mlen;
+	const char *msg;
+
+	if (!ISSET(sppp_auth_role(&pap, sp), SPPP_AUTH_SERV))
+		return;
+
+	rconfid = sp->scp[IDX_PAP].rconfid;
+	if (sp->scp[IDX_PAP].rcr_type == CONF_ACK) {
+		type = PAP_ACK;
+		msg = SUCCMSG;
+		mlen = sizeof(SUCCMSG) - 1;
+	} else {
+		type = PAP_NAK;
+		msg = FAILMSG;
+		mlen = sizeof(FAILMSG) - 1;
+	}
+
+	sppp_auth_send(&pap, sp, type, rconfid,
+	   mlen, (const u_char *)msg, 0);
+
+	if (type == PAP_ACK) {
+		sp->pp_auth_failures = 0;
+	} else {
+		sp->pp_auth_failures++;
+		/* shutdown LCP if auth failed */
+		sppp_wq_add(sp->wq_cp, &sp->scp[IDX_LCP].work_close);
+	}
 }
 
 /*
@@ -5431,6 +5521,68 @@ sppp_auth_send(const struct cp *cp, stru
 	}
 }
 
+static int
+sppp_auth_role(const struct cp *cp, struct sppp *sp)
+{
+	int role;
+
+	role = SPPP_AUTH_NOROLE;
+
+	if (sp->hisauth.proto == cp->proto &&
+	    (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0)
+		SET(role, SPPP_AUTH_SERV);
+
+	if (sp->myauth.proto == cp->proto)
+		SET(role, SPPP_AUTH_PEER);
+
+	return role;
+}
+
+static void
+sppp_auth_to_event(const struct cp *cp, struct sppp *sp)
+{
+	STDDCL;
+	bool override;
+	int state;
+
+	KASSERT(SPPP_WLOCKED(sp));
+
+	override = false;
+	state = sp->scp[cp->protoidx].state;
+
+	if (sp->scp[cp->protoidx].rst_counter > 0) {
+		/* override TO+ event */
+		switch (state) {
+		case STATE_OPENED:
+			if ((sp->hisauth.flags & SPPP_AUTHFLAG_NORECHALLENGE) == 0) {
+				override = true;
+				sp->chap.rechallenging = true;
+				sp->chap.response_rcvd = false;
+				sppp_cp_change_state(cp, sp, STATE_REQ_SENT);
+				cp->scr(sp);
+			}
+			break;
+
+		case STATE_ACK_RCVD:
+			override = true;
+			cp->scr(sp);
+			callout_schedule(&sp->scp[cp->protoidx].ch, sp->lcp.timeout);
+			break;
+		}
+	}
+
+	if (override) {
+		if (debug)
+			log(LOG_DEBUG, "%s: %s TO(%s) rst_counter = %d\n",
+			    ifp->if_xname, cp->name,
+			    sppp_state_name(state),
+			    sp->scp[cp->protoidx].rst_counter);
+		sp->scp[cp->protoidx].rst_counter--;
+	} else {
+		sppp_to_event(sp, __UNCONST(cp));
+	}
+}
+
 /*
  * Send keepalive packets, every 10 seconds.
  */
@@ -6255,12 +6407,12 @@ sppp_phase_network(struct sppp *sp)
 	/* Notify NCPs now. */
 	for (i = 0; i < IDX_COUNT; i++)
 		if ((cps[i])->flags & CP_NCP)
-			(cps[i])->Open(sp, __UNCONST(&cps[i]));
+			sppp_wq_add(sp->wq_cp, &sp->scp[i].work_open);
 
 	/* Send Up events to all NCPs. */
 	for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1)
 		if ((sp->lcp.protos & mask) && ((cps[i])->flags & CP_NCP)) {
-			(cps[i])->Up(sp, __UNCONST(&cps[i]));
+			sppp_wq_add(sp->wq_cp, &sp->scp[i].work_up);
 		}
 
 	/* if no NCP is starting, all this was in vain, close down */
@@ -6465,12 +6617,6 @@ sppp_null(struct sppp *unused)
 }
 
 static void
-sppp_null_event(struct sppp *unused0 __unused, void *unused1 __unused)
-{
-	/* do just nothing */
-}
-
-static void
 sppp_sca_scn(const struct cp *cp, struct sppp *sp)
 {
 	STDDCL;

Index: src/sys/net/if_spppvar.h
diff -u src/sys/net/if_spppvar.h:1.28 src/sys/net/if_spppvar.h:1.29
--- src/sys/net/if_spppvar.h:1.28	Wed Nov 25 09:38:39 2020
+++ src/sys/net/if_spppvar.h	Wed Nov 25 09:46:05 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_spppvar.h,v 1.28 2020/11/25 09:38:39 yamaguchi Exp $	*/
+/*	$NetBSD: if_spppvar.h,v 1.29 2020/11/25 09:46:05 yamaguchi Exp $	*/
 
 #ifndef _NET_IF_SPPPVAR_H_
 #define _NET_IF_SPPPVAR_H_
@@ -91,7 +91,17 @@ struct sauth {
 	char	*secret;		/* secret password */
 	u_char	name_len;		/* no need to have a bigger size */
 	u_char	secret_len;		/* because proto gives size in a byte */
-	char	challenge[16];		/* random challenge [don't change size! it's really hardcoded!] */
+};
+
+struct schap {
+	char	 challenge[16];		/* random challenge
+					   [don't change size! it's really hardcoded!] */
+	char	 digest[16];
+	u_char	 digest_len;
+	bool	 rechallenging;		/* sent challenge after open */
+	bool	 response_rcvd;		/* receive response, stop sending challenge */
+
+	struct sppp_work	 work_challenge_rcvd;
 };
 
 #define IDX_PAP		3
@@ -151,10 +161,6 @@ struct sppp {
 	krwlock_t	pp_lock;	/* lock for sppp structure */
 	int	query_dns;	/* 1 if we want to know the dns addresses */
 	uint32_t	dns_addrs[2];
-#if defined(__NetBSD__)
-	struct	callout ch[IDX_COUNT];	/* per-proto and if callouts */
-	struct	callout pap_my_to_ch;	/* PAP needs one more... */
-#endif
 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
 	struct callout_handle ch[IDX_COUNT]; /* per-proto and if callouts */
 	struct callout_handle pap_my_to_ch; /* PAP needs one more... */
@@ -167,6 +173,7 @@ struct sppp {
 	struct sipcp ipv6cp;		/* IPv6CP params */
 	struct sauth myauth;		/* auth params, i'm peer */
 	struct sauth hisauth;		/* auth params, i'm authenticator */
+	struct schap chap;		/* CHAP params */
 	/*
 	 * These functions are filled in by sppp_attach(), and are
 	 * expected to be used by the lower layer (hardware) drivers

Reply via email to