diff --git a/docs/html/manual.html b/docs/html/manual.html index 924050b..908a276 100644 --- a/docs/html/manual.html +++ b/docs/html/manual.html @@ -183,6 +183,16 @@ sending of RADIUS interim accounting records (in seconds).
This secret will be used in all RADIUS queries. If this is not set then RADIUS queries will fail.
+radius_require_message_authenticator (string)If set to true, RADIUS answers to AccessRequests will have to contain +a valid MessageAuthenticator. If set to auto (default), if the first +RADIUS answer to AccessRequests contains a valid MessageAuthenticator, +subsequent answers will have to contain one. If set to no (not +recommended), RADIUS answers to AccessRequests do not have to contain a +valid MessageAuthenticator. It is advised to set this to true after +checking that your RADIUS server does send MessageAuthenticator.
+radius_authtypes (string)A comma separated list of supported RADIUS authentication methods diff --git a/docs/manpages/startup-config.5 b/docs/manpages/startup-config.5 index 62002d1..086bc79 100644 --- a/docs/manpages/startup-config.5 +++ b/docs/manpages/startup-config.5 @@ -1,4 +1,4 @@ -.\" Automatically generated by Pandoc 3.0.1 +.\" Automatically generated by Pandoc 3.1.3 .\" .\" Define V font for inline verbatim, using C font in formats .\" that render this, and otherwise B font. @@ -360,6 +360,18 @@ RADIUS interim accounting records (in seconds). This secret will be used in all RADIUS queries. If this is not set then RADIUS queries will fail. .PP +\f[B]radius_require_message_authenticator\f[R] (string) +.PP +If set to true, RADIUS answers to AccessRequests will have to contain a +valid MessageAuthenticator. +If set to auto (default), if the first RADIUS answer to AccessRequests +contains a valid MessageAuthenticator, subsequent answers will have to +contain one. +If set to no (not recommended), RADIUS answers to AccessRequests do not +have to contain a valid MessageAuthenticator. +It is advised to set this to true after checking that your RADIUS server +does send MessageAuthenticator. +.PP \f[B]radius_authtypes\f[R] (string) .PP A comma separated list of supported RADIUS authentication methods diff --git a/docs/src/html/manual.md b/docs/src/html/manual.md index 18bee59..e84ef45 100644 --- a/docs/src/html/manual.md +++ b/docs/src/html/manual.md @@ -203,6 +203,18 @@ should be set by a line like: set configstring \"value\" set ipaddress : This secret will be used in all RADIUS queries. If this is not set then RADIUS queries will fail. +`radius_require_message_authenticator` (string) + +: If set to true, RADIUS answers to AccessRequests will have to contain + a valid MessageAuthenticator. + If set to auto (default), if the first RADIUS answer to AccessRequests + contains a valid MessageAuthenticator, subsequent answers will have to + contain one. + If set to no (not recommended), RADIUS answers to AccessRequests do not have + to contain a valid MessageAuthenticator. + It is advised to set this to true after checking that your RADIUS server + does send MessageAuthenticator. + `radius_authtypes` (string) : A comma separated list of supported RADIUS authentication methods diff --git a/docs/src/man/startup-config.5.md b/docs/src/man/startup-config.5.md index 1ca67a2..5df139e 100644 --- a/docs/src/man/startup-config.5.md +++ b/docs/src/man/startup-config.5.md @@ -215,6 +215,18 @@ The following `variables` may be set: This secret will be used in all RADIUS queries. If this is not set then RADIUS queries will fail. +**radius\_require\_message\_authenticator** (string) + + If set to true, RADIUS answers to AccessRequests will have to contain + a valid MessageAuthenticator. + If set to auto (default), if the first RADIUS answer to AccessRequests + contains a valid MessageAuthenticator, subsequent answers will have to + contain one. + If set to no (not recommended), RADIUS answers to AccessRequests do not have + to contain a valid MessageAuthenticator. + It is advised to set this to true after checking that your RADIUS server + does send MessageAuthenticator. + **radius\_authtypes** (string) A comma separated list of supported RADIUS authentication methods ("pap" or "chap"), in order of preference (default "pap"). diff --git a/etc/startup-config.default b/etc/startup-config.default index 496522e..65101cf 100644 --- a/etc/startup-config.default +++ b/etc/startup-config.default @@ -34,6 +34,8 @@ set primary_radius 10.0.0.3 #set secondary_radius 0.0.0.0 #set secondary_radius_port 1812 set radius_secret "secret" +# Set this to yes once you have confirmed that your RADIUS server provides MessageAuthenticator in its responses +#set radius_require_message_authenticator auto # Acceptable authentication types (pap, chap) in order of preference #set radius_authtypes "pap" diff --git a/l2tpns.c b/l2tpns.c index 9d0aa4f..47c731b 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -166,6 +166,7 @@ config_descriptt config_values[] = { CONFIG("radius_accounting", radius_accounting, BOOL), CONFIG("radius_interim", radius_interim, INT), CONFIG("radius_secret", radiussecret, STRING), + CONFIG("radius_require_message_authenticator", radius_require_message_authenticator, STRING), CONFIG("radius_authtypes", radius_authtypes_s, STRING), CONFIG("radius_dae_port", radius_dae_port, SHORT), CONFIG("radius_bind_min", radius_bind_min, SHORT), diff --git a/l2tpns.h b/l2tpns.h index 253633e..8a54fc5 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -732,6 +732,7 @@ typedef struct int ppp_keepalive; // send echoes regardless char radiussecret[64]; + char radius_require_message_authenticator[5]; int radius_accounting; int radius_interim; in_addr_t radiusserver[MAXRADSERVER]; // radius servers diff --git a/radius.c b/radius.c index 1f9ea9e..e5bf495 100644 --- a/radius.c +++ b/radius.c @@ -32,6 +32,10 @@ extern ip_filtert *ip_filters; static const hasht zero; +static enum { + MAYBE = 0, YES, NO, +} radius_sends_message_authenticator[MAXRADSERVER]; + static void calc_auth(const void *buf, size_t len, const uint8_t *in, uint8_t *out) { MD5_CTX ctx; @@ -163,7 +167,7 @@ void radiussend(uint16_t r, uint8_t state) uint8_t b[4096]; // RADIUS packet char pass[129]; int pl; - uint8_t *p; + uint8_t *p, *ma = NULL; sessionidt s; CSTAT(radiussend); @@ -237,6 +241,16 @@ void radiussend(uint16_t r, uint8_t state) memcpy(b + 4, radius[r].auth, 16); p = b + 20; + + if (state == RADIUSAUTH || state == RADIUSJUSTAUTH) + { + *p = 80; // MessageAuthenticator + p[1] = 18; // length + ma = p+2; + memset(ma, 0, 16); // Zero for the computation + p += p[1]; + } + if (s) { *p = 1; // user name @@ -464,12 +478,18 @@ void radiussend(uint16_t r, uint8_t state) // All AVpairs added *(uint16_t *) (b + 2) = htons(p - b); + if (state != RADIUSAUTH && state != RADIUSJUSTAUTH) { // Build auth for accounting packet calc_auth(b, p - b, zero, b + 4); memcpy(radius[r].auth, b + 4, 16); } + + // Compute MessageAuthenticator + if (ma) + MD5_Hmac(ma, b, p-b, config->radiussecret, strlen(config->radiussecret)); + memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; *(uint32_t *) & addr.sin_addr = config->radiusserver[(radius[r].try - 1) % config->numradiusservers]; @@ -587,6 +607,83 @@ void processrad(uint8_t *buf, int len, char socket_index) if (radius[r].state == RADIUSAUTH || radius[r].state == RADIUSJUSTAUTH) { + // First check MessageAuthenticator + { + uint8_t *p = buf + 20; + uint8_t *e = buf + len; + int seen = 0, want; + + for (; p + 2 <= e && p[1] && p + p[1] <= e; p += p[1]) + { + if (*p == 80) + { + // MessageAuthenticator + uint8_t auth[16]; + uint8_t authcheck[16]; + uint8_t vector[16]; + + /* Set original vector for computation */ + memcpy(vector, buf+4, 16); + memcpy(buf+4, radius[r].auth, 16); + + /* Set authenticator to zero for computation */ + memcpy(auth, p+2, 16); + memset(p+2, 0, 16); + + MD5_Hmac(authcheck, buf, len, config->radiussecret, strlen(config->radiussecret)); + if (memcmp(auth, authcheck, 16) != 0) + { + LOG(1, 0, 0, "Incorrect MessageAuthenticator in Access reply (wrong secret in radius config?)\n"); + return; + } + + /* Restore vector */ + memcpy(buf+4, vector, 16); + + /* Restore authenticator */ + memcpy(p+2, auth, 16); + + seen = 1; + } + } + + if (!strcmp(config->radius_require_message_authenticator, "no")) + want = 0; + else if (!strcmp(config->radius_require_message_authenticator, "yes")) + want = 1; + else + { + if (config->radius_require_message_authenticator[0] + && strcmp(config->radius_require_message_authenticator, "auto")) + LOG(1, 0, 0, "Unrecognized radius_require_message_authenticator '%s', assuming auto\n", + config->radius_require_message_authenticator); + + int num = radius[r].try % config->numradiusservers; + + switch (radius_sends_message_authenticator[num]) { + case YES: + want = 1; + break; + case NO: + want = 0; + break; + case MAYBE: + want = seen; + if (seen) + radius_sends_message_authenticator[num] = YES; + else + radius_sends_message_authenticator[num] = NO; + break; + } + } + + if (want && !seen) + { + LOG(1, 0, 0, "Missing MessageAuthenticator in Access reply\n"); + return; + } + } + // run post-auth plugin struct param_post_auth packet = { &tunnel[t],