Add lcp_renegotiation option

To support proxy LCP negotiation.

Note: we *have* to take the auth id from the proxy answer, otherwise we would
replay previous ids, for which the client might cache the answer and thus
ignore our new challenge and just repeat their outdated answer.
This commit is contained in:
Samuel Thibault 2025-03-08 20:09:33 -05:00
parent 817ce35748
commit e7db528544
6 changed files with 388 additions and 31 deletions

126
l2tpns.c
View file

@ -159,6 +159,7 @@ config_descriptt config_values[] = {
CONFIG("ppp_max_configure", ppp_max_configure, INT),
CONFIG("ppp_max_failure", ppp_max_failure, INT),
CONFIG("ppp_keepalive", ppp_keepalive, BOOL),
CONFIG("lcp_renegotiation", lcp_renegotiation, STRING),
CONFIG("primary_dns", default_dns1, IPv4),
CONFIG("secondary_dns", default_dns2, IPv4),
CONFIG("primary_radius", radiusserver[0], IPv4),
@ -4163,9 +4164,18 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr, uint16_t indexu
uint16_t message = 0xFFFF; // message type
uint8_t fatal = 0;
uint8_t mandatory = 0;
uint8_t *last_sent_lcp_confreq = 0;
uint16_t last_sent_lcp_confreq_n = 0;
uint8_t *last_received_lcp_confreq = 0;
uint16_t last_received_lcp_confreq_n = 0;
uint16_t atype = 0;
uint16_t authid = 0;
char authname[MAXUSER] = "";
char authchall[MAXPASS] = "";
size_t authchalln = 0;
char authresp[MAXPASS] = "";
size_t authrespn = 0;
uint16_t asession = 0; // assigned session
uint32_t amagic = 0; // magic number
uint8_t aflags = 0; // flags from last LCF
uint16_t version = 0x0100; // protocol version (we handle 0.0 as well and send that back just in case)
char called[MAXTEL] = ""; // called number
char calling[MAXTEL] = ""; // calling number
@ -4642,50 +4652,70 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr, uint16_t indexu
}
case 29: // Proxy Authentication Type
{
uint16_t atype = ntohs(*(uint16_t *)b);
atype = ntohs(*(uint16_t *)b);
LOG(4, s, t, " Proxy Auth Type %u (%s)\n", atype, ppp_auth_type(atype));
break;
}
case 30: // Proxy Authentication Name
{
char authname[64];
memset(authname, 0, sizeof(authname));
memcpy(authname, b, (n < sizeof(authname)) ? n : sizeof(authname) - 1);
LOG(4, s, t, " Proxy Auth Name (%s)\n",
authname);
if (n < sizeof(authname))
{
LOG(4, s, t, " Proxy Auth Name (%s)\n", authname);
}
else
{
LOG(2, s, t, " Proxy Auth Name too long (%s)\n", authname);
}
break;
}
case 31: // Proxy Authentication Challenge
{
LOG(4, s, t, " Proxy Auth Challenge\n");
if (n <= sizeof(authchall))
{
memcpy(authchall, b, n);
authchalln = n;
LOG(4, s, t, " Proxy Auth Challenge\n");
}
else
{
LOG(2, s, t, " Proxy Auth Challenge too long\n");
}
break;
}
case 32: // Proxy Authentication ID
{
uint16_t authid = ntohs(*(uint16_t *)(b));
authid = ntohs(*(uint16_t *)(b));
LOG(4, s, t, " Proxy Auth ID (%u)\n", authid);
break;
}
case 33: // Proxy Authentication Response
LOG(4, s, t, " Proxy Auth Response\n");
break;
case 27: // last sent lcp
{ // find magic number
uint8_t *p = b, *e = p + n;
while (p + 1 < e && p[1] && p + p[1] <= e)
{
if (n <= sizeof(authresp))
{
if (*p == 5 && p[1] == 6) // Magic-Number
amagic = ntohl(*(uint32_t *) (p + 2));
else if (*p == 7) // Protocol-Field-Compression
aflags |= SESSION_PFC;
else if (*p == 8) // Address-and-Control-Field-Compression
aflags |= SESSION_ACFC;
p += p[1];
memcpy(authresp, b, n);
authrespn = n;
LOG(4, s, t, " Proxy Auth Response\n");
}
else
{
LOG(2, s, t, " Proxy Auth Response too long\n");
}
break;
}
case 27: // last sent lcp
{
last_sent_lcp_confreq = b;
last_sent_lcp_confreq_n = n;
break;
}
break;
case 28: // last recv lcp confreq
break;
{
last_received_lcp_confreq = b;
last_received_lcp_confreq_n = n;
break;
}
case 26: // Initial Received LCP CONFREQ
break;
case 39: // seq required - we control it as an LNS anyway...
@ -4996,9 +5026,8 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr, uint16_t indexu
break;
case 12: // ICCN
LOG(3, s, t, "Received ICCN\n");
if (amagic == 0) amagic = time_now;
session[s].magic = amagic; // set magic number
session[s].flags = aflags; // set flags received
session[s].magic = time_now; // set magic number
session[s].mru = PPPoE_MRU; // default
controlnull(t); // ack
@ -5013,8 +5042,39 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr, uint16_t indexu
else
sess_local[s].mp_epdis = 0;
sendlcp(s, t);
change_state(s, lcp, RequestSent);
if (last_sent_lcp_confreq_n && last_received_lcp_confreq_n &&
processlcpproxy(s, t, last_sent_lcp_confreq, last_sent_lcp_confreq_n,
last_received_lcp_confreq, last_received_lcp_confreq_n)
&& ( (sess_local[s].lcp_authtype == 0)
|| (sess_local[s].lcp_authtype == AUTHPAP && authrespn)
|| (sess_local[s].lcp_authtype == AUTHCHAP && authchalln == 16 && authrespn == 16)))
{
// Could reuse the LCP negotiation and don't have empty proxy auth response or unknown challenge size, don't renegotiate
LOG(3, s, t, "Reusing LCP negotiation\n");
// Start with proxy auth id to avoid client caching challenge responses
sess_local[s].auth_id = authid;
if (!sess_local[s].lcp_authtype)
{
// No auth, open immediately
lcp_open(s, t);
}
else
{
// Process auth
session[s].ppp.phase = Authenticate;
change_state(s, lcp, Opened);
processauthproxy(s, t, atype, authname, authchalln, authchall, authrespn, authresp);
}
}
else
{
// Have to renegotiate LCP
LOG(3, s, t, "Renegotiating LCP\n");
sendlcp(s, t);
change_state(s, lcp, RequestSent);
}
break;
case 14: // CDN
@ -7425,6 +7485,16 @@ static void update_config()
MSS = MRU - TCP_HDRS;
MSS6 = MRU - TCP6_HDRS;
if (!config->lcp_renegotiation[0])
strcpy(config->lcp_renegotiation, "always");
if (strcmp(config->lcp_renegotiation, "always") &&
strcmp(config->lcp_renegotiation, "on-mismatch"))
{
LOG(0, 0, 0, "Invalid LCP renegotiation type %s, assuming 'always'\n", config->lcp_renegotiation);
strcpy(config->lcp_renegotiation, "always");
}
// Update radius
config->numradiusservers = 0;
for (i = 0; i < MAXRADSERVER; i++)