Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Tassilo Schweyer 2025-05-03 10:55:58 +02:00
commit 54d36d7512
5 changed files with 548 additions and 187 deletions

588
ppp.c
View file

@ -556,6 +556,437 @@ static void ppp_code_rej(sessionidt s, tunnelidt t, uint16_t proto,
tunnelsend(buf, l + (q - buf), t);
}
// Process received LCP options from o from packet p.
// Set changed to 1 if we changed configuration that should be sent to cluster slaves, fill response on nak or rej
static void processreceivedlcpconfreq(sessionidt s, tunnelidt t, uint8_t *p, uint8_t *o, uint16_t x, int *changed, uint8_t **response)
{
static uint8_t asyncmap[4] = { 0, 0, 0, 0 }; // all zero
static uint8_t authproto[5];
uint8_t b[MAXETHER];
uint8_t *q = NULL;
while (x > 2)
{
int type = o[0];
int length = o[1];
if (length == 0 || length == 1 || type == 0 || x < length) break;
switch (type)
{
case 1: // Maximum-Receive-Unit
{
uint16_t mru = ntohs(*(uint16_t *)(o + 2));
if (mru >= MINMTU)
{
session[s].mru = mru;
(*changed)++;
break;
}
LOG(3, s, t, " Remote requesting MRU of %u. Rejecting.\n", mru);
mru = htons(MRU);
q = ppp_conf_nak(s, b, sizeof(b), PPPLCP, response, q, p, o, (uint8_t *) &mru, sizeof(mru));
}
break;
case 2: // Async-Control-Character-Map
if (!ntohl(*(uint32_t *)(o + 2))) // all bits zero is OK
break;
LOG(3, s, t, " Remote requesting asyncmap. Rejecting.\n");
q = ppp_conf_nak(s, b, sizeof(b), PPPLCP, response, q, p, o, asyncmap, sizeof(asyncmap));
break;
case 3: // Authentication-Protocol
{
int proto = ntohs(*(uint16_t *)(o + 2));
char proto_name[] = "0x0000";
int alen;
if (proto == PPPPAP)
{
if (config->radius_authtypes & AUTHPAP)
{
sess_local[s].lcp_authtype = AUTHPAP;
break;
}
strcpy(proto_name, "PAP");
}
else if (proto == PPPCHAP)
{
if (config->radius_authtypes & AUTHCHAP
&& *(o + 4) == 5) // MD5
{
sess_local[s].lcp_authtype = AUTHCHAP;
break;
}
strcpy(proto_name, "CHAP");
}
else
sprintf(proto_name, "%#4.4x", proto);
LOG(3, s, t, " Remote requesting %s authentication. Rejecting.\n", proto_name);
alen = add_lcp_auth(authproto, sizeof(authproto), config->radius_authprefer);
if (alen < 2) break; // paranoia
q = ppp_conf_nak(s, b, sizeof(b), PPPLCP, response, q, p, o, authproto + 2, alen - 2);
if (q && **response == ConfigNak &&
config->radius_authtypes != config->radius_authprefer)
{
// alternate type
alen = add_lcp_auth(authproto, sizeof(authproto), config->radius_authtypes & ~config->radius_authprefer);
if (alen < 2) break;
q = ppp_conf_nak(s, b, sizeof(b), PPPLCP, response, q, p, o, authproto + 2, alen - 2);
}
break;
}
break;
case 4: // Quality-Protocol
case 5: // Magic-Number
case 7: // Protocol-Field-Compression
case 8: // Address-And-Control-Field-Compression
break;
case 17: // Multilink Max-Receive-Reconstructed-Unit
{
uint16_t mrru = ntohs(*(uint16_t *)(o + 2));
session[s].mrru = mrru;
(*changed)++;
LOG(3, s, t, " Received PPP LCP option MRRU: %d\n",mrru);
}
break;
case 18: // Multilink Short Sequence Number Header Format
{
session[s].mssf = 1;
(*changed)++;
LOG(3, s, t, " Received PPP LCP option MSSN format\n");
}
break;
case 19: // Multilink Endpoint Discriminator
{
uint8_t epdis_class = o[2];
int addr;
session[s].epdis.addr_class = epdis_class;
session[s].epdis.length = length - 3;
if (session[s].epdis.length > 20)
{
LOG(1, s, t, "Error: received EndDis Address Length more than 20: %d\n", session[s].epdis.length);
session[s].epdis.length = 20;
}
for (addr = 0; addr < session[s].epdis.length; addr++)
session[s].epdis.address[addr] = o[3+addr];
(*changed)++;
switch (epdis_class)
{
case LOCALADDR:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis Local Address Class: %d\n",epdis_class);
break;
case IPADDR:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis IP Address Class: %d\n",epdis_class);
break;
case IEEEMACADDR:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis IEEE MAC Address Class: %d\n",epdis_class);
break;
case PPPMAGIC:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis PPP Magic No Class: %d\n",epdis_class);
break;
case PSNDN:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis PSND No Class: %d\n",epdis_class);
break;
default:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis NULL Class %d\n",epdis_class);
}
}
break;
default: // Reject any unknown options
LOG(3, s, t, " Rejecting unknown PPP LCP option %d\n", type);
q = ppp_conf_rej(s, b, sizeof(b), PPPLCP, response, q, p, o);
}
x -= length;
o += length;
}
}
// Process sent LCP options from o
// Set changed to 1 if we changed configuration that should be sent to cluster slaves
static int processsentlcpconfreq(sessionidt s, tunnelidt t, uint8_t *p, uint8_t *o, uint16_t x, int *changed)
{
int auth = 0;
int ok = 1;
while (x > 2)
{
int type = o[0];
int length = o[1];
if (length == 0 || length == 1 || type == 0 || x < length) break;
switch (type)
{
case 1: // Maximum-Receive-Unit
{
uint16_t mru = ntohs(*(uint16_t *)(o + 2));
if (mru >= MINMTU)
{
sess_local[s].ppp_mru = mru;
break;
}
ok = 0;
break;
}
case 2: // Async-Control-Character-Map
if (ntohl(*(uint32_t *)(o + 2)))
ok = 0;
break;
case 3: // Authentication-Protocol
{
int proto = ntohs(*(uint16_t *)(o + 2));
if (proto == PPPPAP)
{
if (config->radius_authtypes & AUTHPAP)
{
auth = sess_local[s].lcp_authtype = AUTHPAP;
break;
}
ok = 0;
}
else if (proto == PPPCHAP)
{
if (config->radius_authtypes & AUTHCHAP
&& *(o + 4) == 5) // MD5
{
auth = sess_local[s].lcp_authtype = AUTHCHAP;
break;
}
ok = 0;
}
else
ok = 0;
}
break;
case 4: // Quality-Protocol
break;
case 5: // Magic-Number
if (length < 6) break;
session[s].magic = ntohl(*(uint32_t *)(o + 2));
*changed = 1;
break;
case 7: // Protocol-Field-Compression
session[s].flags |= SESSION_PFC;
*changed = 1;
break;
case 8: // Address-And-Control-Field-Compression
session[s].flags |= SESSION_ACFC;
*changed = 1;
break;
case 17: // Multilink Max-Receive-Reconstructed-Unit
sess_local[s].mp_mrru = ntohs(*(uint16_t *)(o + 2));
break;
case 18: // Multilink Short Sequence Number Header Format
sess_local[s].mp_mssf = 1;
break;
case 19: // Multilink Endpoint Discriminator
ok = 0;
break;
default: // Reject any unknown options
ok = 0;
break;
}
x -= length;
o += length;
}
if (!ok)
// Return after taking note of magic and flags
return 0;
if (sess_local[s].lcp_authtype && !auth)
// We do want authentication, we have to check with client
return 0;
if (sess_local[s].mp_epdis)
// We do want Endpoint Discriminator
return 0;
return 1;
}
// Try to use LCP proxy to avoid renegotiating it (in case the LAC doesn't support passing LCP through)
int processlcpproxy(sessionidt s, tunnelidt t, uint8_t *sent_lcp, uint16_t sent_lcp_n, uint8_t *received_lcp, uint16_t received_lcp_n)
{
uint8_t *response = 0;
int changed = 0;
if (!strcmp(config->lcp_renegotiation, "always"))
/* We always renegotiate */
return 0;
LOG(3, s, t, "Trying to reuse the LAC sent and received LCP configuration:\n");
if (config->debug >= 3)
{
dumplcp(sent_lcp, sent_lcp_n);
dumplcp(received_lcp, received_lcp_n);
}
/* Check what configuration the LAC sent */
if (!processsentlcpconfreq(s, t, NULL, sent_lcp, sent_lcp_n, &changed))
{
LOG(3, s, t, "Sent LCP conf does not match\n");
return 0;
}
/* Check what configuration the LAC received */
processreceivedlcpconfreq(s, t, NULL, received_lcp, received_lcp_n, &changed, &response);
if (response)
{
LOG(3, s, t, "Received LCP conf does not match\n");
/* Mismatch, renegotiate */
return 0;
}
LOG(3, s, t, "LCP conf match\n");
if (changed)
cluster_send_session(s);
// Ok!
return 1;
}
// Try to use auth proxy to avoid renegotiating it (in case the LAC forces PAP)
int processauthproxy(sessionidt s, tunnelidt t,
uint16_t authtype, const char *authname,
size_t authchalln, const char authchall[authchalln],
size_t authrespn, const char authresp[authrespn])
{
uint16_t r;
if ((sess_local[s].lcp_authtype == 0 && authtype != 0)
|| (sess_local[s].lcp_authtype == AUTHPAP && authtype != 3) // PAP
|| (sess_local[s].lcp_authtype == AUTHCHAP && authtype != 2)) // CHAP
{
LOG(3, s, t, "Authentication proxy mismatch: got %u (%s) while expecting %u\n",
authtype, ppp_auth_type(authtype), sess_local[s].lcp_authtype);
return 0;
}
if (session[s].ppp.phase != Authenticate)
{
LOG(3, s, t, "Already authenticated\n");
return 1;
}
if (sess_local[s].lcp_authtype == 0)
{
LOG(4, s, t, "No auth needed\n");
return 1;
}
if ((!config->disable_lac_func) && lac_conf_forwardtoremotelns(s, authname))
{
// Creating a tunnel/session has been started
return 1;
}
if (!(r = radiusnew(s)))
{
LOG(1, s, t, "No RADIUS session available to authenticate session...\n");
sessionshutdown(s, "No free RADIUS sessions.", CDN_UNAVAILABLE, TERM_SERVICE_UNAVAILABLE);
return 1;
}
// Run PRE_AUTH plugins
struct param_pre_auth packet = { &tunnel[t], &session[s], strdup(authname), NULL, 0, 1 };
if (sess_local[s].lcp_authtype == AUTHPAP)
{
packet.password = strndup(authresp, authrespn);
packet.protocol = PPPPAP;
}
else if (sess_local[s].lcp_authtype == AUTHCHAP)
{
if (authchalln != 16)
{
LOG(1, s, t, "CHAP challenge size different from 16: %zu\n", authchalln);
return 0;
}
if (authrespn != 16)
{
LOG(1, s, t, "CHAP response size different from 16: %zu\n", authrespn);
return 0;
}
packet.password = calloc(17, 1);
memcpy(packet.password, authresp, 16);
packet.protocol = PPPCHAP;
}
else
// Not PAP or CHAP??
return 0;
run_plugins(PLUGIN_PRE_AUTH, &packet);
if (!packet.continue_auth)
{
LOG(3, s, t, "A plugin rejected PRE_AUTH\n");
if (packet.username) free(packet.username);
if (packet.password) free(packet.password);
return 1;
}
// Take all authentication information from proxy, and record for possibly proxying ourself
strncpy(session[s].user, packet.username, sizeof(session[s].user) - 1);
strncpy((char *) sess_local[s].auth_name, packet.username, sizeof(sess_local[s].auth_name) - 1);
if (sess_local[s].lcp_authtype == AUTHPAP)
{
strncpy(radius[r].pass, packet.password, sizeof(radius[r].pass) - 1);
strncpy((char *) sess_local[s].auth_resp, packet.password, sizeof(sess_local[s].auth_resp) - 1);
}
else
{
memcpy(radius[r].auth, authchall, 16);
memcpy(sess_local[s].auth_chall, authchall, 16);
memcpy(radius[r].pass, packet.password, 16);
memcpy(sess_local[s].auth_resp, packet.password, 16);
radius[r].chap = 1;
}
radius[r].id = sess_local[s].auth_id;
LOG(3, s, t, "Sending login for %s to RADIUS\n", packet.username);
free(packet.username);
free(packet.password);
if ((session[s].mrru) && (!first_session_in_bundle(s)))
radiussend(r, RADIUSJUSTAUTH);
else
radiussend(r, RADIUSAUTH);
return 1;
}
// Process LCP messages
void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
{
@ -648,165 +1079,10 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
}
else if (*p == ConfigReq)
{
int x = l - 4;
uint8_t *o = (p + 4);
uint8_t *response = 0;
static uint8_t asyncmap[4] = { 0, 0, 0, 0 }; // all zero
static uint8_t authproto[5];
int changed = 0;
while (x > 2)
{
int type = o[0];
int length = o[1];
if (length == 0 || length == 1 || type == 0 || x < length) break;
switch (type)
{
case 1: // Maximum-Receive-Unit
{
uint16_t mru = ntohs(*(uint16_t *)(o + 2));
if (mru >= MINMTU)
{
session[s].mru = mru;
changed++;
break;
}
LOG(3, s, t, " Remote requesting MRU of %u. Rejecting.\n", mru);
mru = htons(MRU);
q = ppp_conf_nak(s, b, sizeof(b), PPPLCP, &response, q, p, o, (uint8_t *) &mru, sizeof(mru));
}
break;
case 2: // Async-Control-Character-Map
if (!ntohl(*(uint32_t *)(o + 2))) // all bits zero is OK
break;
LOG(3, s, t, " Remote requesting asyncmap. Rejecting.\n");
q = ppp_conf_nak(s, b, sizeof(b), PPPLCP, &response, q, p, o, asyncmap, sizeof(asyncmap));
break;
case 3: // Authentication-Protocol
{
int proto = ntohs(*(uint16_t *)(o + 2));
char proto_name[] = "0x0000";
int alen;
if (proto == PPPPAP)
{
if (config->radius_authtypes & AUTHPAP)
{
sess_local[s].lcp_authtype = AUTHPAP;
break;
}
strcpy(proto_name, "PAP");
}
else if (proto == PPPCHAP)
{
if (config->radius_authtypes & AUTHCHAP
&& *(o + 4) == 5) // MD5
{
sess_local[s].lcp_authtype = AUTHCHAP;
break;
}
strcpy(proto_name, "CHAP");
}
else
sprintf(proto_name, "%#4.4x", proto);
LOG(3, s, t, " Remote requesting %s authentication. Rejecting.\n", proto_name);
alen = add_lcp_auth(authproto, sizeof(authproto), config->radius_authprefer);
if (alen < 2) break; // paranoia
q = ppp_conf_nak(s, b, sizeof(b), PPPLCP, &response, q, p, o, authproto + 2, alen - 2);
if (q && *response == ConfigNak &&
config->radius_authtypes != config->radius_authprefer)
{
// alternate type
alen = add_lcp_auth(authproto, sizeof(authproto), config->radius_authtypes & ~config->radius_authprefer);
if (alen < 2) break;
q = ppp_conf_nak(s, b, sizeof(b), PPPLCP, &response, q, p, o, authproto + 2, alen - 2);
}
break;
}
break;
case 4: // Quality-Protocol
case 5: // Magic-Number
case 7: // Protocol-Field-Compression
case 8: // Address-And-Control-Field-Compression
break;
case 17: // Multilink Max-Receive-Reconstructed-Unit
{
uint16_t mrru = ntohs(*(uint16_t *)(o + 2));
session[s].mrru = mrru;
changed++;
LOG(3, s, t, " Received PPP LCP option MRRU: %d\n",mrru);
}
break;
case 18: // Multilink Short Sequence Number Header Format
{
session[s].mssf = 1;
changed++;
LOG(3, s, t, " Received PPP LCP option MSSN format\n");
}
break;
case 19: // Multilink Endpoint Discriminator
{
uint8_t epdis_class = o[2];
int addr;
session[s].epdis.addr_class = epdis_class;
session[s].epdis.length = length - 3;
if (session[s].epdis.length > 20)
{
LOG(1, s, t, "Error: received EndDis Address Length more than 20: %d\n", session[s].epdis.length);
session[s].epdis.length = 20;
}
for (addr = 0; addr < session[s].epdis.length; addr++)
session[s].epdis.address[addr] = o[3+addr];
changed++;
switch (epdis_class)
{
case LOCALADDR:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis Local Address Class: %d\n",epdis_class);
break;
case IPADDR:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis IP Address Class: %d\n",epdis_class);
break;
case IEEEMACADDR:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis IEEE MAC Address Class: %d\n",epdis_class);
break;
case PPPMAGIC:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis PPP Magic No Class: %d\n",epdis_class);
break;
case PSNDN:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis PSND No Class: %d\n",epdis_class);
break;
default:
LOG(3, s, t, " Received PPP LCP option Multilink EndDis NULL Class %d\n",epdis_class);
}
}
break;
default: // Reject any unknown options
LOG(3, s, t, " Rejecting unknown PPP LCP option %d\n", type);
q = ppp_conf_rej(s, b, sizeof(b), PPPLCP, &response, q, p, o);
}
x -= length;
o += length;
}
processreceivedlcpconfreq(s, t, p, p + 4, l - 4, &changed, &response);
if (changed)
cluster_send_session(s);