SquirrelMail Authentication

Integrating the Filter Management System into SquirrelMail

Requirements

Method

The Filter Management System requires a username and a password, in order to properly ensure the user is who they say they are. To integrate the security of SquirrelMail into the FMS, we must identify the methods SquirrelMail uses to store and retrieve passwords for authentication purposes. This requires a trace of the SquirrelMail code to identify the interface mechanism.

Once the identity has been obtained, we develop a solution for integrating this identity into the FMS software, and properly add this to the add.pm package.

Once the solution is integrated, the templates/links for editing the filters need to be altered in SquirrelMail to link to the FMS software.

Method Notes on Obtaining the Password

Prior to entering the starting point, we must know what we need to watch for. For example, in dismantling unix password data, we keep our eyes open for the crypt() function, as it is an integral part of understanding how the software works.

Performing research (web research) on SquirrelMail login processes fails to reveal any information as to what we need to look for. This project begins with the research into login problems and issues rather than processes, as bugs should be publicly available, and will almost always provide a starting point for debugging the process. Beginning here, we learn that prior to php4.1, SquirrelMail used HTTP_SESSION_VARS variables to hold login information, and after 4.1 should have used session_register to set these variables. On doing a grep for session_register, we find nothing. Reducing the grep term to register, we reveal that most variable calls to sqsession_register.

Once we know what we are watching for, we begin to trace down where the passwords are stored, and how we access them. This must begin at the standard "login" screen. We determine where the document is by first connecting to SquirrelMail. Once we have a login screen, we examine the url to access the page, and find http://mail.yourdomain.com/webmail/src/login.php. With this in hand, we open up a shell to the webmail server, and change into the document root of the web server configuration. For instance, you might find yourself in /var/www/html (linux) or /usr/local/www/data (FreeBSD).

We change into the URI portion of the url without the script name (webmail/src/). Editing the document login.php reveals more information. The only handler in the document prints a form, containing the variables username and secretkey. It submits the information to redirect.php.

When we examine the redirect.php script, we search for secretkey, as that is the password field we are interested in. On grep-ing through the redirect.php, we find the following matching lines of code.
    if (isset($_POST['secretkey'])) { $secretkey = $_POST['secretkey']; $onetimepad = OneTimePadCreate(strlen($secretkey)); $key = OneTimePadEncrypt($secretkey, $onetimepad);
With these lines, we see that a global variable is set $secretkey, which is used in a specialized function OneTimePadCreate. If we open up the file itself and look at all of these instances, we find that the first two matching lines are grouped together and primarily set the global variable. The next occurances of the secretkey line use that global variable, and are also grouped together. These lines are followed by a sqsession_register. Obviously, this is the code that stores the password we are looking for.

When we look for this OneTimePadCreate function, we will find it in the parent directory's subdir functions, in a file called ../functions/strings.php. This function creates a completely new string that is the same length of the password. So, this function is not the culprit, but is involved in the encryption method on the next line as the "key", and is then stored in the session information file.

So, we examine the rest of that section of code looking for a $key variable, and find lines :
    $imapConnection = sqimap_login($login_username, $key, $imapServerAddress, $imapPort, 0); setcookie('key', $key, 0, $base_uri);
Login, and you should now have a cookie set for your web server called "key". This can be decrypted by copying the OneTimePadDecrypt function and accessing the OneTimePadCreate. When we examine the decryption function (also in ../functions/strings.php), we get the following code:
    function OneTimePadDecrypt ($string, $epad) { $pad = base64_decode($epad); $encrypted = base64_decode ($string); $decrypted = ''; for ($i = 0; $i < strlen ($encrypted); $i++) { $decrypted .= chr (ord($encrypted[$i]) ^ ord($pad[$i])); } return $decrypted; }
Now, to re-write this into perl, we must first abstract it into pseudo-code. The resulting pseudo-code function is :
    function ([encoded password], [key]) base64 decode [key] loop on each charatcer in [encoded password] exclusive or it with the corresponding character in the [key] end loop return the resulting password end function
Now, in perl, I thought I'd just split the incoming parameters into arrays, and then process the information. Plus, we could always use MIME::base64 to reduce our code size, but just in case you don't have access to the MIME::base64 module :
    sub base64_decode { $_[0] =~ tr#A-Za-z0-9+/##cd; $_[0] =~ tr#A-Za-z0-9+/# -_#; my $len = pack("c", 32 + 0.75*length($_[0])); return unpack("u", $len . $_[0]); } sub decode_password { my ($p,$k) = @_; my @password = split(//,base64_decode($p)); my @key = split(//,base64_decode($k)); my $decrypted_password = ''; my $count = 0; ### decrypt the password my $size = scalar(@password); $size = scalar(@key) if (scalar(@key) < $size); while ($count < $size) { print "pass [$password[$count]] key [$key[$count]]\n"; $decrypted_password .= ($password[$count] ^ $key[$count]); $count++; } return $decrypted_password; }
Now, that was a keyboard full. But, for you hackers out there, that is the basic gist of decrypting the passwords.

Method Notes on Integrating the password check in FMS

Now we're getting somewhere. We place our function into the filter management script's auth.pm module, and put the base64_decode function into cgi (as that's where it should be). Then, we edit the main function to check if there is a username/password submission (already there), then check the SQMSESSID variable and it's session file for the appropriate information. If that succeeds, set the username key and write the password.

Methods for Editing the SquirrelMail code to point to us

This is fairly simple. Looking at the "Filter" links in the options folder will produce a url. Grep for this url, and take the most common (happens to be plugins/filters/setup.php). In side of that script, close to the bottom, it lists some "url" fields, complete with instructions. Remove the second addition, and then alter the first to point to the FMS scripts, altering the description and control methods.

Summary

Setting up the proper integration takes time, and can be an effective method for connecting systems. With the above document, we have discussed (and implemented) solutions for controlling Exim/SquirrelMail using a third party program. The FMS software is available from Joe Lewis, the author, and custom integrations are indeed available.

Template and Content © Joe Lewis