Menu

Coova Chilli JSON Interface

Anonymous

JSON interface of coova chilli

Background

It is assumed that the reader is familiar with coova chilli, and already has a compiled version on a server.
It is also assumed that the reader has a reasonable background on web technologies as well as the Internet's workings.
It is also assumed where the reader is asked to go to the URLs containing the 10.1.0.1 IP that they are doing it from a client machine to the coova chilli captive portal.

Introduction

The coova chilli daemon has a few interfaces. The least understood is the JSON interface. The JSON interface usually makes use of CHAP passwords and by default coova chilli will make use of coova's own AAA services (Authentication, Authorization and Accounting).
Things get more interesting when one moves away from using the defaults and start using your own AAA services. (Read FreeRADIUS).

In this document

This document will explain how the coova chilli daemon gets its configuration options.

  1. It then discusses the internal web server of the coova chilli daemon.
  2. Following on this, it looks at the JSON interface and the Javascript used to interact with it.
  3. Finally it looks at the modifications required to be able to use PAP passwords through the Javascript JSON objects.
  4. We use the terms 'JSON interface' and 'JSON service' interchangeably, but they refer to the same thing.

Configuration options

The coova chilli daemon has a special start-up script. Each time this script runs, it creates a new configuration file for the coova chilli daemon.
The following is a table with a list of configuration files and the role they play.
To keep the first column short we assume this is under '/usr/local/etc'

File
Role

chilli.conf
The main config file which the coova chilli deamon will use. This file will source various secondary configuration files under the ./chilli directory. It will also define the script to run when the chilli program has started and the script to run upon shut-down.

/chilli/main.conf
This file gets automatically generated, thus you should not make changes to it as it will be overwritten.

./chilli/hs.conf
This file is empty – Gets created by startup script

./chilli/local.conf
This file is empty – Gets created by startup script

./chilli/defaults
This file contains the default values that will be used by the ./chilli/functions to produce the ./chilli/main.conf file.

./chilli/config
This starts out as a copy of the ./chilli/defaults file and are subsequently modified by the user. This file is used together with the defaults file to produce the ./chilli/main.conf file when the start-up script is run. In this document we will make use of the ./chilli/config file to configure the options to the chilli daemon.

It may be worth to note here that you are not forced to use this set-up.
You may very well make use of you own chilli.conf file which will not source any secondary files.
You should then also adapt your start-up script for this scenario.
The original chilli program's configuration file works perfect with the coova chilli daemon.

The internal web server

The coova chilli daemon upon start-up creates its own web server.
The default will be under http://10.1.0.1:3990/www
Where this contents is served out from is defined by

HS_WWWDIR=/usr/local/etc/chilli/www

This can change, but the /www convention in the URL will always remain. So if you change it to:

HS_WWWDIR=/usr/local/etc/chilli/xxx

the internal web server will still be reachable by the /www extension, but it will serve the content under the 'xxx' directory.
This internal web server also has the ability to serve dynamic content, much like PHP or the classic CGI interface on the Apache web server.
The internal web server does this by making use of a program called 'haserl'. This program has a very small footprint and can thus be easily used in embedded devices like the Linksys routers.
Make sure you do have this program installed on the machine you intend to run coova chilli on.
You can grab a copy of the source at:
http://haserl.sourceforge.net/
To start the haserl process to serve the dynamic content, you need to define a script that the coova chilli daemon needs to run.
This is defined by the

HS_WWWBIN=/usr/local/etc/chilli/wwwsh

value.
You can check if the dynamic web content generation is functional by running this script and giving it one of the .chi (indicating it is a haserl script) as argument
/usr/local/etc/chilli/wwwsh /usr/local/etc/www/chillijs.chi

This should produce some Javascript output on the terminal.

If you use the haserl program to generate dynamic content you also have to use the default mechanism of generation the configuration files. The haserl setup in coova chilli reads the 'default' and 'config' files to extract values which it then replaces to reflect the specific set-up in the Javascript that it serves.
The internal web server does not need to be used, but if you want to make use of the JSON interface it is essential to have a fully working one.
When we discuss the JSON interface we will take a look of the essential files that needs to be present in the 'www' directory.

The JSON interface

Introduction

JSON services is a modern way to transfer data from a web server to a browser. It has less overhead than XML which is typically used in AJAX applications.
Using JSON you also have the ability to do cross domain data sourcing.
This is a limitation on the XHTTP design used by most AJAX frameworks and libraries.
Yahoo and other Internet service providers makes use of this technology to deliver services.
You can use Javascript to fetch certain info from the services they provide, like the GIS info of a physical address.
This can then be dynamically be fed into your own webpage's content.
Check out this link for more on JSON web services:
http://www.xml.com/pub/a/2005/12/21/json-dynamic-script-tag.html
This is a very neat feature which enable us to put the login page of coova chilli on any web page that can be seen by our client using the captive portal.
All you need is to include a line of Javascript code to the page you want to use as the login page and you're done.

JSON in coova chilli

Now that you are all exited about this JSON thing, lets see how it works on coova chilli.
Coova chilli runs a JSON service under the 'json' directory.
So if you want to check the status of yourself, you can go to this URL
http://10.1.0.1:3990/json/status
The return will look like this:

{"version":"1.0","clientState":0,"challenge":"99313fb9b08e430b3e30f13b12787285",
"location":{"name":"My HotSpot"},
"redir":{"originalURL":"","redirectionURL":"","macAddress":"00-0C-F9-F-57-A1"}}

You can make use of this JSON interface to authenticate yourself, get status about your connection, or log yourself out of the captive portal.
To communicate with the JSON interface on coova chilli, we make use of a Javascript library.
/usr/local/etc/chilli/www/ChilliLibrary.js
It contains the chilliController object.
This object needs to be set-up to reflect the values defined for our specific set-up.
In order to do this we make use of the internal web interface's haserl scripts.
For our login page to work correct we need to do the following:

Define a captive portal page

The captive portal page is defined by this value

HS_UAMFORMAT=http://\$HS_UAMSERVER:\$HS_UAMPORT/www/index.html

Here we define a file called 'index.html' but it can be any file on any server reachable by the client's machine. (You may have to open the walled garden for this)
There is another page which are defined.

HS_UAMHOMEPAGE=http://\$HS_UAMLISTEN:\$HS_UAMPORT/www/coova.html

This page is typically a splash type of page which will then redirect to '/prelogin' service of the coova chilli daemon.
This will then redirect the client to the page defined in the 'HS_UAMFORMAT' variable.

Create a captive portal page

To keep everything in one place I suggest you serve the captive portal page out of the internal web server (which saves you installing Appache :)!)
The captive portal page needs a special piece of Javascript. I call it the 'bootup' Javascript.
This calls other pieces of Javascript which in turn calls their own respective pieces of Javascript.
When all is done, everything is loaded and all the libraries are present in order to communicate with the JSON interface of the coova chilli daemon.
For the index.html page we can take the one used by coova's AAA service.
It will inform the user if it is not accessed via the captive portal. Here is the page.
You may want to customize it to display your own company's detail.

<html>
<head>
<!--
    A purely HTML based captive portal using the JSON interface of CoovaChilli
-->
<title>coova hotspot</title>
<style><!--
    body,td,a,p,h{
        font-family:arial,sans-serif;
    }
    body {
        text-align: center;
        padding-top: 30px;
        margin: auto;
        width: 50%;
    }
    #MyChilli {
        background: url("/images/coova.jpg") right top no-repeat;
        margin: auto;
        text-align: left;
        padding: 10px 0 30px 0;
    }
    #locationName {
        height: 50px;
        font-size: 120%;
        font-weight: bold;
    }
    #chilliPage {
        border: 1px solid orange;
        padding: 20px 20px 20px 20px;
        margin-top: 20px;
    }
    #signUpRow {
        display: inline;
    }
-->
</style>
</head>
<body>
<div id="MyChilli">
<div id="noLocation" style="display:none;">
<p style="padding-top: 100px;"><strong>You are not at a hotspot.</strong>
If you want to see a a sample login page using the
<a href="http://coova.org/wiki/index.php/CoovaChilli/JSON">JSON interface</a> of <a href="http://coova.org/wiki/index.php/CoovaChilli">CoovaChilli</a>
, then <a href="javascript: window.location = 'view-source:' + window.location.href;">view the source</a> of this page.</p>
</div>
<script id='chillijs' src='chilli.js'></script>
</div>
</body>
</html>

The Javascript that is sourced are also those used by coova's AAA service.
Here's the contents of the chilli.js file

if (navigator.appVersion.indexOf("MSIE")!=-1)
    document.write("<script type='text/javascript' id='chillicontroller'></script>");
if (!window.queryObj) {
    window.queryObj = new Object();
    window.location.search.replace(new RegExp("([^?=&]+)(=([^&]*))?","g"), function($0,$1,$2,$3) { queryObj[$1] = $3; });
}
if (queryObj['uamip'] != null && queryObj['uamport'] != null) {
    var script = document.getElementById('chillicontroller');
    if (script == null) {
        script = document.createElement('script');
        script.id = 'chillicontroller';
        script.type = 'text/javascript';
        script.src = 'http://'+queryObj['uamip']+':'+queryObj['uamport']+'/www/chillijs.chi';
        var head = document.getElementsByTagName("head")[0];
        if (head == null) head = document.body;
        head.appendChild(script);
    }
    script.src = 'http://'+queryObj['uamip']+':'+queryObj['uamport']+'/www/chillijs.chi';
} else {
    var noLocation = document.getElementById("noLocation");
    if (noLocation != null && noLocation.style) {
        noLocation.style.display = 'inline';
    }
}

How it all fits in together

This section will describe how you end up with with a complete JSON enabled login page. It lists each step and indicate which files are involved.

User tries to access the Internet.

We assume that the user got an IP from the coova chilli daemon, and navigates to a web page.
If the 'HS_UAMHOMEPAGE' value is defined, it will redirect the user first to this page.
As mentioned before, this is typically a 'splash' type page which will then redirect the user to the '/prelogin' service.

User gets a captive portal page.

The '/prelogin' service will redirect the user to the page defined as the HS_UAMFORMAT variable.
This will then be the index.html page which we created.
It will be called with a query string containing info such as the IP and port that the coova chilli daemon runs on, as well as the site the user tried to connect to.

index.html calls chilli.js

The index.html page sources the chilli.js file.
This piece of Javascript gets the IP and port on which the coova chilli daemon runs from the window.location object. (The URL of the page which includes the query string added by the '/prelogin' service.)
It will use this info to get the /www/chillijs.chi file.
The content of /www/chilli.chi gets dynamically created by the haserl program to include coova chilli specifics specified in the configuration files.
If the Javascript code could not get a valid IP and port from the URL of the page's query string, it assumes the page was not called by the '/prelogin' service, and will display a message informing the user about this.

chillijs.ch calls chillijs.chi.sh

Chillijs.chi.sh is a shell script which does the following.
Run config.sh which will source the variables defined in 'defaults' and 'config' files.
Sources the ChilliLibrary.js file.
Set certain attributes of the chilliController object based on the values received from the config.sh script.
Sources the chilliController.js file.
As a note, if you are curious to see more detail on the JSON communication between the captive portal page and the coova chilli daemon's JSON interface, you can activate Firebug in Firefox and add
echo "chilliController.debug = true;"
to the chillijs.chi.sh script.
This will output dedug info to Firebug's console.

chillController.js calls chilliform.chi

The chilliController.js file creates a complete login and status page from Javascript.
To accomplish this it also sources chillifrom.chi as a Javascript object.
Chilliform.chi calls chillifrom.chi.sh
Chilliform.chi.sh is a shell script which sources the json_html.tmpl file and replace the innerHTML of the 'loginForm' object with this text.

User gets his logon page

After all the above is completed the user is faced with a logon page asking for their credentials.

Using FreeRADIUS PAP passwords and JSON

If you make use of your own RADIUS servers for authentication, you are most likely to use PAP passwords.
Hooking up your RADIUS server with Novell Netware's e-Directory's LDAP server forces you to use PAP passwords (Unless you activate Universal passwords)
Various other LAMP front-ends to the FreeRADIUS's database back-ends also makes use of PAP passwords.
The code in the ChilliLibrary.js uses CHAP (Challenge and Reply protocol) but you can with little effort get it to work with PAP. We need a few things in place for this.

Create a UAM JSON web service

This may sound very impressive but it is actually very simple.
You need to set up a HTTPS web server with a cgi interface.
Then in the directory where the cgi content gets served from you add the following Perl script.
In Ubuntu it is under '/usr/lib/cgi-bin'.
The script is called uam.pl, but you can call it whatever you want.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#! /usr/bin/perl -w
use strict;
use Digest::MD5  qw(md5 md5_hex md5_base64);
#Same as HS_UAMSECRET in config file
my $uamsecret = "greatsecret";
my $js_header = "Content-type: text/javascript\n\n";
# Make sure that the get query parameters are clean
my $OK_CHARS='-a-zA-Z0-9_.@&=%!';
$_ = my $query=$ENV{QUERY_STRING};
s/[^$OK_CHARS]/_/go;
$query = $_;

my $return = '';
my ($username,$password,$challenge);

#Read query parameters which we care about
my @array = split('&',$query);
foreach my $var ( @array )
{
    my @array2 = split('=',$var);
    if ($array2[0] =~ /^username$/i) { $username = $array2[1]; }
    if ($array2[0] =~ /^password$/i) { $password = $array2[1]; }
    if ($array2[0] =~ /^challenge$/) { $challenge = $array2[1]; }
}
$password =~ s/\+/ /g;
$password =~s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/seg;
my $hexchal  = pack "H32", $challenge;
my $newchal  = md5($hexchal, $uamsecret);
my $pappassword = unpack "H32", ($password ^ $newchal);
print $js_header;
print "chilliJSON.reply({'response':'".$pappassword."'})";

This script needs to be called with us a username, password and challenge eg
https://10.1.0.1/cgi-bin/uam.pl?username=alee&password=alee&challenge=fgkljlrf
It will then return a JSON object which can be used by the chilliController object.
chilliJSON.reply({'response':'55ac9f17a22b8abfc7201d9fa24239d0'})
You may wonder why this has to be HTTPS.
The password will be passed in cleartext, thus to prevent other people to potentially sniff the packets and obtaining a username and password pair, we have to use HTTPS.
You may also note that compared to the JSON interface's feedback of the coova chilli daemon there is now a 'chilliJSON.reply' wrapper around it.
The coova chilli daemon is intelligent and check how it gets called.
If it gets called as a Javascript object it will include the 'chilliJSON.reply'.
If it gets called from a web page, it will exclude it.
The 'chillJSON.reply' is a callback function which gets called as soon as the response is completed.

Modify the ChilliLibrary.js to accommodate PAP passwords

The logon query to the JSON service for CHAP passwords looks like this:

logon?username=$username&response=$response

The logon query for PAP passwords looks like this

logon?username=$username&password=$pappassword

where $pappasword is the value returned by the UAM JSON web service for the challenge from the coova chilli daemon.
You need to modify the ChilliLibrary.js file to authenticate with PAP by changing the following line under 'chilliController.logonStep3'.

'chilliController.logonStep3'.
/* Build /logon command URL */
//var logonUrl = chilliController.urlRoot() + 'logon?username=' + escape(username) + '&response='  + resp.response;
var logonUrl = chilliController.urlRoot() + 'logon?username=' + escape(username) + '&password='  + resp.response;

This will now attempt to logon using PAP instead of CHAP.

Define the UAMSERVICE

Specify where the JSON UAM web service reside by defining the following setting:

HS_UAMSERVICE=https://10.1.0.1/cgi-bin/uam.pl

Set HS_UAMSECRET

Ensure the value of HS_UAMSECRET is the same as in the uam.pl file.

HS_UAMSECRET=greatsecret

Conclusion

With the above in place, you should now be able to use PAP passwords with the JSON interface of coova chilli.


Related

Wiki: YfiTechCoovaLogin