Stochastic Geometry

September 9, 2009

Maximum Password Length in Coova-Chilli

Filed under: C,General,Linux,Perl — Mark Dennehy @ 17:43

Bad Day So for the last few days I’ve been banging my head up against the same problem in the lab. We currently use Chillispot to run our access points for the Metakall user trials, which is grand, but it’s also fairly out of date – it hasn’t been actively developed on since 1997 – and it has some serious security issues. So with the new academic year looming, we’re moving over to its successor, Coova-Chilli.

Coova has a few neat features we like, and it’s also being actively maintained, which is important. So I took our testbed server, installed FreeRADIUS and MySQL and Apache and all the custom stuff we’ve written and generally got things set up, and then went to install the latest version of coova-chilli (1.0.14). There was an hour of swearing at iptables as usual (I really am coming to hate that program), and then I thought that’d be that.

But of course not. The Metakall client wouldn’t log in. At first I thought the iptables setup was wrong, but no, all’s well there. FreeRADIUS was fine, so it had to be something in Coova-Chilli. Ah-ha, says I, it’s that new-fangled JSON captive portal setup, that’s the problem, I’ll dig into that. A day and a half later, and I’ve gone through the JSON portal stuff and can’t find anything wrong.

Exasperated, I switch Coova back to the old Chillispot configuration files (which are compatible), bypassing all the new JSON stuff and still there’s a problem.

It’s at this point that I try to log in manually, which no-one’s done on the system since we got the client working. And I notice that usernames are passed perfectly to FreeRADIUS. And so are passwords… up until you feed in more than 15 characters to the password field. Ah-ha. Check the HTML forms… but no, the password field size is fine. I’m half-relieved at that, that I didn’t miss it while digging through the JSON stuff.

Okay, now we’re ticked off. As we’ve got the tarball extracted already, dive into src/ and start digging. Where does it read in the password field? Eventually found it, in src/redir.c and found it calls a function called hextochar2() on the query’s data in case the password stuff is encoded in hex in the URL. And that function can only cope with 15 characters at a time and acts wierd if more than 15 characters show up. Since the client always provides a password longer than that, FreeRADIUS was getting garbage all the time in the password field and it wasn’t down to the captive portal using CHAP or MS-CHAP like I’d thought was causing the hassle. It was a bug in Coova-Chilli itself.


Okay, google time. And has a few items in the wiki… but when you click on the links, they tell you that the site’s been moved to and you’re bounced to the homepage. There’s no google cache of the page and the wayback machine doesn’t help either. Annoying.

So sign up to the development list, ask a question about this and get a response in three minutes that it’s been fixed in SVN. Some days you wouldn’t know whether to curse or cry or both…

So SVN checkout and rebuild from scratch (neatly avoiding the whoopsie where I accidentally overwrite the config files with make install and lose another hour redoing old work) and try again, and yes, now 16 character passwords are handled properly!

…but 17-character passwords are truncated to 16 characters, as are longer passwords.

Some days, you do know whether to curse or cry…

Okay, back into the code and check redir_hextochar() again. Seems okay… Hmmm. Over there in redir_radius() we have this:

for (m=0; m < RADIUS_PWSIZE;)
  for (n=0; n < REDIR_MD5LEN; m++, n++)
    user_password[m] = conn->password[m] ^ chap_challenge[n];

It’s a neat use of variable scope to repeat its way through the chap_challange[] array if conn->password[] is longer than chap_challange[]. The problem though, is that that has to be duplicated on the webpage sending the password across, and when you check that (in this case the hotspotlogin.cgi legacy perl script), you find this:

  $hexchal  = pack "H32", $challenge;


  } elsif (defined($userpassword)) {
  # Encode plain text password with challenge
  # (which may or may not be uamsecret encoded)

  $pappassword = unpack "H32", ($password ^ $newchal);

  $logonUrl = "http://$uamip:$uamport/logon?username=$username&password=$pappassword";

There are two bugs here: firstly that calling pack() and unpack() with the template “H32” means that no matter how long the list you pass in (in this case $challange and ($password ^ $newchal) ), the output is going to be 16 bytes every time, the rest is just discarded.

Changing the template to “H*” cures that, but exposes the second bug which is that if $password is longer than $newchal, the bitwise string XOR operator in perl just pads out $newchal with imaginary zeros to get the job done. So the tail of your output is the plaintext of the tail of your input – and when that gets over to redir_radius(), which does the job properly, it tries to XOR the result with the challange and gets back only the head of $password – the tail is mangled.

So we just duplicate the algorithm from redir.c and repeat $newchal until it’s long enough that that doesn’t happen, like so:

  $hexchal  = pack "H*", $challenge;


  } elsif (defined($userpassword)) {
  # Encode plain text password with challenge
  # (which may or may not be uamsecret encoded)

  while (length($newchal) < length($password)){
   $newchal .= $newchal;

$pappassword = unpack "H*", ($password ^ $newchal);
And that’s it. Tested, everything works, solution emailed to the coova development list and patch submitted.We’ll run on the patched svn version until 1.0.15 gets released.

Mind you, I’d feel a lot better if I’d found this last Thursday…

Here’s the patch.diff file for anyone interested, svn checkout revision 217 of Coova-Chilli and apply it:

Index: doc/hotspotlogin.cgi
--- doc/hotspotlogin.cgi        (revision 217)
+++ doc/hotspotlogin.cgi        (working copy)
@@ -150,7 +150,7 @@

 print "Content-type: text/html\n\n";

-    $hexchal  = pack "H32", $challenge;
+    $hexchal  = pack "H*", $challenge;

 if (defined $uamsecret) {
 $newchal  = md5($hexchal, $uamsecret);
@@ -170,8 +170,13 @@
 # Encode plain text password with challenge
 # (which may or may not be uamsecret encoded)

-       $pappassword = unpack "H32", ($password ^ $newchal);
+       # If challange isn't long enough, repeat it until it is
+       while (length($newchal) < length($password)){
+               $newchal .= $newchal;
+        }

+       $pappassword = unpack "H*", ($password ^ $newchal);
 $logonUrl = "http://$uamip:$uamport/logon?username=$username&password=$pappassword";

 } else {


  1. Thanks for posting your trials and tribulations. I am trying to get around the 16 character limit as well, but I am hoping that I don’t have to install the full blown coova-chili install.
    Right now I am using dd-wrt v24.sp2 to redirect clients to hotspotlogin.cgi which is located on a separate radius server. I tried replacing my hotspotlogin.cgi script with the latest version of hotspotlogin.cgi (1.14 build 240) which contains your fix, but passwords are still trimmed to 16 characters. Am I missing the point? Do I really need to fix the version of chillispot running on dd-wrt AND the hotspotlogin.cgi?

    Since I doubt my ability to rebuild and install chillispot from source in the DD-wrt environment, would you recommend installing coovachilli on my radius server and running everything from there?

    Thanks again for sharing with the rest of us! BTW, the gif at the top is pretty funny.


    Comment by John S — November 3, 2009 @ 20:57 | Reply

  2. Unfortunately you have to fix both the cgi script and the chillispot server John. There were errors on both sides with encoding/decoding the password when it exceeded 16 characters. I’ve been working with coova, but I’m not as au fait with it as I am with chillispot. Still, chillispot’s a dead project with no development work on it since ’07, so moving over might well be a positive thing to do.

    Comment by Mark Dennehy — November 3, 2009 @ 21:31 | Reply

  3. Thanks for the help, from what I could see you need to change REDIR_MD5LEN in the redir.h file
    May also need to change the REDIR_CHALLEN


    Comment by Tim — November 10, 2009 @ 17:17 | Reply

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Please log in using one of these methods to post your comment: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at

%d bloggers like this: