uspot: add MS CHAP support

* extend the radius-client to also support MS CHAP

Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
John Crispin
2022-09-01 14:06:33 +02:00
parent 68689674b7
commit 63b5803086
4 changed files with 168 additions and 24 deletions

View File

@@ -21,30 +21,43 @@ let uam = require('uam');
// give a client access to the internet
function allow_client(ctx) {
system('ubus call spotfilter client_set \'{ "interface": "hotspot", "address": "' + replace(ctx.mac, '-', ':') + '", "state": 1, "dns_state": 1}\'');
include('allow.uc', ctx);
if (ctx.query_string.userurl)
include('uam.uc', { uam_location: ctx.query_string.userurl });
else
include('allow.uc', ctx);
}
// log the client in via radius
function auth_client(ctx) {
if (!ctx.query_string.username || !ctx.query_string.password) {
include('allow.uc', ctx);
return false;
}
let password = uam.password(uam.md5(config.uam.challenge, ctx.mac), ctx.query_string.password, config.uam.uam_secret);
let cfg = fs.open('/tmp/' + ctx.mac + '.json', 'w');
cfg.write({
type: 'uam-auth',
let password;
let payload = {
server: sprintf('%s:%s:%s',config.radius.auth_server, config.radius.auth_port, config.radius.auth_secret),
username: ctx.mac,
password,
acct_session: "0123456789",
client_ip: ctx.env.REMOTE_ADDR,
called_station: ctx.mac,
calling_station: config.uam.nasmac,
nas_ip: ctx.env.SERVER_ADDR,
nas_id: config.uam.nasid
});
};
if (ctx.query_string.username && ctx.query_string.response) {
let challenge = uam.md5(config.uam.challenge, ctx.mac);
payload.type = 'uam-chap-auth';
payload.username = ctx.query_string.username;
payload.chap_password = ctx.query_string.response;
if (config.uam.secret)
payload.chap_challenge = uam.chap_challenge(challenge, config.uam.uam_secret);
else
payload.chap_challenge = challenge;
} else if (ctx.query_string.username && ctx.query_string.password) {
payload.type = 'uam-auth';
payload.username = ctx.mac;
payload.password = uam.password(uam.md5(config.uam.challenge, ctx.mac), ctx.query_string.password, config.uam.uam_secret);
}
let cfg = fs.open('/tmp/' + ctx.mac + '.json', 'w');
cfg.write(payload);
cfg.close();
let stdout = fs.popen('/usr/bin/radius-client /tmp/' + ctx.mac + '.json');
@@ -82,10 +95,9 @@ global.handle_request = function(env) {
// split QUERY_STRING
if (env.QUERY_STRING)
for (let chunk in split(env.QUERY_STRING, '&')) {
let var = split(chunk, '=');
if (length(var) != 2)
continue;
ctx.query_string[var[0]] = var[1];
let m = match(chunk, /^([^=]+)=(.*)$/);
if (!m) continue;
ctx.query_string[m[1]] = replace(m[2], /%([[:xdigit:]][[:xdigit:]])/g, (m, h) => chr(hex(h) || 0x20));
}
auth_client(ctx);
};

View File

@@ -193,10 +193,9 @@ global.handle_request = function(env) {
// split QUERY_STRING
if (env.QUERY_STRING)
for (let chunk in split(env.QUERY_STRING, '&')) {
let var = split(chunk, '=');
if (length(var) != 2)
continue;
ctx.query_string[var[0]] = var[1];
let m = match(chunk, /^([^=]+)=(.*)$/);
if (!m) continue;
ctx.query_string[m[1]] = replace(m[2], /%([[:xdigit:]][[:xdigit:]])/g, (m, h) => chr(hex(h) || 0x20));
}
// recv POST data

View File

@@ -13,6 +13,8 @@ enum {
RADIUS_SERVER,
RADIUS_USERNAME,
RADIUS_PASSWORD,
RADIUS_CHAP_PASSWORD,
RADIUS_CHAP_CHALLENGE,
RADIUS_ACCT_SESSION,
RADIUS_CLIENT_IP,
RADIUS_CALLED_STATION,
@@ -27,6 +29,8 @@ static const struct blobmsg_policy radius_policy[__RADIUS_MAX] = {
[RADIUS_SERVER] = { .name = "server", .type = BLOBMSG_TYPE_STRING },
[RADIUS_USERNAME] = { .name = "username", .type = BLOBMSG_TYPE_STRING },
[RADIUS_PASSWORD] = { .name = "password", .type = BLOBMSG_TYPE_STRING },
[RADIUS_CHAP_PASSWORD] = { .name = "chap_password", .type = BLOBMSG_TYPE_STRING },
[RADIUS_CHAP_CHALLENGE] = { .name = "chap_challenge", .type = BLOBMSG_TYPE_STRING },
[RADIUS_ACCT_SESSION] = { .name = "acct_session", .type = BLOBMSG_TYPE_STRING },
[RADIUS_CLIENT_IP] = { .name = "client_ip", .type = BLOBMSG_TYPE_STRING },
[RADIUS_CALLED_STATION] = { .name = "called_station", .type = BLOBMSG_TYPE_STRING },
@@ -40,6 +44,8 @@ static struct config {
char *server;
char *username;
char *password;
char chap_password[17];
char chap_challenge[16];
char *acct_session;
struct sockaddr_in client_ip;
char *called_station;
@@ -51,6 +57,26 @@ static struct config {
static struct blob_buf b = {};
static struct blob_attr *tb[__RADIUS_MAX] = {};
static int
str_to_hex(char *in, char *out, int olen)
{
int ilen = strlen(in);
int len = 0;
while (ilen >= 2 && olen > 1) {
int c;
sscanf(in, "%2x", &c);
*out++ = (char) c;
in += 2;
ilen -= 2;
len++;
}
*out = '\0';
return len;
}
static int
result(rc_handle const *rh, int accept, VALUE_PAIR *pair)
{
@@ -93,6 +119,14 @@ config_load(void)
if (tb[RADIUS_PASSWORD])
config.password = blobmsg_get_string(tb[RADIUS_PASSWORD]);
if (tb[RADIUS_CHAP_PASSWORD]) {
*config.chap_password = '\0';
str_to_hex(blobmsg_get_string(tb[RADIUS_CHAP_PASSWORD]), &config.chap_password[1], 16);
}
if (tb[RADIUS_CHAP_CHALLENGE])
str_to_hex(blobmsg_get_string(tb[RADIUS_CHAP_CHALLENGE]), config.chap_challenge, 16);
if (tb[RADIUS_ACCT_SESSION])
config.acct_session = blobmsg_get_string(tb[RADIUS_ACCT_SESSION]);
@@ -221,6 +255,64 @@ uam_auth(void)
return result(rh, 0, NULL);
}
static int
uam_chap_auth(void)
{
VALUE_PAIR *send = NULL, *received;
rc_handle *rh = NULL;
if (!config.server || !config.username ||
!config.acct_session || !config.called_station ||
!config.calling_station || !config.nas_id)
return result(NULL, 0, NULL);
rh = radius_init();
if (!rh)
return result(NULL, 0, NULL);
if (rc_avpair_add(rh, &send, PW_USER_NAME, config.username, -1, 0) == NULL)
return result(rh, 0, NULL);
if (rc_avpair_add(rh, &send, PW_CHAP_PASSWORD, config.chap_password, 17, 0) == NULL)
return result(rh, 0, NULL);
if (rc_avpair_add(rh, &send, PW_CHAP_CHALLENGE, config.chap_challenge, 16, 0) == NULL)
return result(rh, 0, NULL);
if (rc_avpair_add(rh, &send, PW_ACCT_SESSION_ID, config.acct_session, -1, 0) == NULL)
return result(rh, 0, NULL);
if (rc_avpair_add(rh, &send, PW_FRAMED_IP_ADDRESS, &config.client_ip.sin_addr, 4, 0) == NULL)
return result(rh, 0, NULL);
//if (rc_avpair_add(rh, &send, PW_NAS_PORT_TYPE, , -1, 0) == NULL)
// return result(rh, 0, NULL);
//if (rc_avpair_add(rh, &send, PW_NAS_PORT, , -1, 0) == NULL)
// return result(rh, 0, NULL);
// if (rc_avpair_add(rh, &send, PW_NAS_PORT_ID_STRING, , -1, 0) == NULL)
// return result(rh, 0, NULL);
if (rc_avpair_add(rh, &send, PW_CALLED_STATION_ID, config.called_station, -1, 0) == NULL)
return result(rh, 0, NULL);
if (rc_avpair_add(rh, &send, PW_CALLING_STATION_ID, config.calling_station, -1, 0) == NULL)
return result(rh, 0, NULL);
if (rc_avpair_add(rh, &send, PW_NAS_IP_ADDRESS, &config.nas_ip.sin_addr, 4, 0) == NULL)
return result(rh, 0, NULL);
if (rc_avpair_add(rh, &send, PW_NAS_IDENTIFIER, config.nas_id, -1, 0) == NULL)
return result(rh, 0, NULL);
rc_apply_config(rh);
if (rc_auth(rh, 0, send, &received, NULL) == OK_RC)
return result(rh, 1, received);
return result(rh, 0, NULL);
}
static int
uam_acct(void)
{
@@ -298,6 +390,9 @@ main(int argc, char **argv)
if (!strcmp(config.type, "uam-auth"))
return uam_auth();
if (!strcmp(config.type, "uam-chap-auth"))
return uam_chap_auth();
if (!strcmp(config.type, "uam-acct"))
return uam_acct();

View File

@@ -12,7 +12,7 @@
#define MIN(a, b) ((a > b) ? b : a)
static int
hex_to_str(char *in, char *out, int olen)
str_to_hex(char *in, char *out, int olen)
{
int ilen = strlen(in);
int len = 0;
@@ -31,6 +31,15 @@ hex_to_str(char *in, char *out, int olen)
return len;
}
static void
hex_to_str(char *in, char *out, int len)
{
int i;
for (i = 0; i < len; i++)
sprintf(&out[i * 2], "%02x", in[i]);
}
static uc_value_t *
uc_password(uc_vm_t *vm, size_t nargs)
{
@@ -50,8 +59,8 @@ uc_password(uc_vm_t *vm, size_t nargs)
if (!_chal || !_pass || !_secret)
return ucv_boolean_new(false);
plen = hex_to_str(ucv_to_string(vm, _pass), pass, sizeof(pass));
clen = hex_to_str(ucv_to_string(vm, _chal), chal, sizeof(chal));
plen = str_to_hex(ucv_to_string(vm, _pass), pass, sizeof(pass));
clen = str_to_hex(ucv_to_string(vm, _chal), chal, sizeof(chal));
secret = ucv_to_string(vm, _secret);
md5_begin(&md5);
@@ -66,6 +75,34 @@ uc_password(uc_vm_t *vm, size_t nargs)
}
static uc_value_t *
uc_challenge(uc_vm_t *vm, size_t nargs)
{
uc_value_t *_chal = uc_fn_arg(0);
uc_value_t *_secret = uc_fn_arg(1);
char chal[32];
char uamchal[32];
char ret[33] = {};
int clen;
char *secret;
md5_ctx_t md5 = {};
if (!_chal || !_secret)
return ucv_boolean_new(false);
clen = str_to_hex(ucv_to_string(vm, _chal), chal, sizeof(chal));
secret = ucv_to_string(vm, _secret);
md5_begin(&md5);
md5_hash(chal, clen, &md5);
md5_hash(secret, strlen(secret), &md5);
md5_end(uamchal, &md5);
hex_to_str(uamchal, ret, 16);
return ucv_string_new(ret);
}
static uc_value_t *
uc_md5(uc_vm_t *vm, size_t nargs)
{
@@ -97,6 +134,7 @@ uc_md5(uc_vm_t *vm, size_t nargs)
static const uc_function_list_t global_fns[] = {
{ "password", uc_password },
{ "chap_challenge", uc_challenge },
{ "md5", uc_md5 },
};