mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-10-29 17:42:41 +00:00
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:
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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 },
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user