Latest Tweets

Timeout reading a signed email using SquirrelMail 1.4.17 & G/PGP Plugin 2.1

The issue

While reading a signed GPG email message inside SquirrelMail with the G/PGP plug-in installed, it causes an enormous time-out. This time-out could look like a hang up, despite the fact the Apache process is running perfectly well and other users don’t detect any problems concerning SquirrelMail. This behaviour can be reproduced as long as any user is trying to read a GPG signed email message, no matter on which mail-box.

A brief pre-analysis

Let msg be a GPG signed email message, stored at the “Sent” mail-box. When it is read, the screen shows no data. Using Firebug, it is feasible to determine the total amount of time passed by starting reading the email till the email body appears on the screen:

Validating a GPG signature time out issue

Validating a GPG signature time out issue

As shown previously, a total amount of 43,73 seconds was totally annoying from a user’s point of view. Doing more tests, depending on the size and the message structure, could be possible to even increase this enormous time till two minutes.

Dealing with the G/PGP plug-in sources – again 😉 –

All G/PGP plug-in code can be found on plugins/gpg/ directory. The procedures involved in reading an email message – no matter if it is encrypted or signed or whatever -, are done by the SquirrelMail read_body.php source file. Then, depending on the plugins installed, there are certain hooks, fired as soon as they are needed. In this particular case, all gpg hooks are defined and implemented on the source file stored at plugins/gpg/gpg_hook_functions.php.

This file has two functions in charge of checking a valid GPG signature as long as the email message contains one, as shown below:

function gpg_check_sign_pgp_mime(&$message,$fullbodytext) {
...
}
function gpg_check_sign($body,$encrypted=false) {
...
}

These pair of functions are called when a user is opening read_body.php, in order to read an email message which has a GPG signature. This way, the entire G/PGP plug-in will be able to determine when this signature is valid or not, as any GPG frontend must provide as a functionality. The first one will be executed when the message contains MIME data – that is, as long as there is attachments, HTML code, or these sort of things inside it -, or the second one when there is not – usually, with text/plain signed messages -.

The P/GPG plugin call the gpg command so as to achieve that. When debugging the code, a process “gpg” shows up on the Linux Box running apache http server:

3 apache   27626  0.0  0.0  3480 1128 ?        SL   09:21   0:00 /usr/bin/gpg –command-fd 7 –status-fd 5 –no-tty –yes     –openpgp –homedir ../data/user.gnupg –verify /tmp/GP
4 G3cd2RM –

The entire gpg 0,1, and 2 file descriptors – as many others -, will be through pipes, as clearly is shown below thanks to the lsof command:

(…)

15 gpg     27861 apache    0r  FIFO    0,5         21410190 pipe
16 gpg     27861 apache    1w  FIFO    0,5         21410191 pipe
17 gpg     27861 apache    2w  FIFO    0,5         21410192 pipe
18 gpg     27861 apache    3r   REG    9,0     260   309869 /tmp/GPGuTPJEt
19 gpg     27861 apache    5w  FIFO    0,5         21410193 pipe
20 gpg     27861 apache    7r  FIFO    0,5         21410194 pipe

(…)

Debugging the gpg command using strace, the total amount of time doing all tasks needed to verify a GPG signature was really outrageous. Clearly, the process didn’t hang up, but the time was extremely high. Therefore, this was the culprit.

Fixing the problem

At first, I was wondering why such plug-in was in need of running – using php api – any command trough pipes. It was uncommon, coming to think of it. Php has , obviously, the exec_* functions, also, so I read all sources carefully, again, to find out. Soon, this piece of code came out:

     66     if (!check_php_version(4,3) || ($use_proc_open!='true')) {
     67         if ($debug) { echo "Setting GPG object to force use of exec to communicate with gpg.
\n" };
     68         $gpgexec->force_exec=true;
     69     }

So, there was a choice to run gpg not using pipes anymore. That was what I needed to do, so I needed to alter the use_proc_open variable to be false. The PHP version was superior to 4.3, indeed. Some code lines earlier, this option was clearly read from a plain options file, stored at plugins/gpg/gpg_system_defaults.txt. This is shown right away:

     39     $use_proc_open = $GLOBALS['GPG_SYSTEM_OPTIONS']['use_proc_open'];
     32     if (!isset($path_to_gpg)) {
     33         if (!isset($GLOBALS['GPG_SYSTEM_OPTIONS']['path_to_gpg'])) {
     34                 load_prefs_from_file(SM_PATH . 'plugins/gpg/gpg_system_defaults.txt',$debug);
     35                 load_prefs_from_file(SM_PATH . 'plugins/gpg/gpg_local_prefs.txt',$debug);
     36         }
     37             $path_to_gpg=($GLOBALS['GPG_SYSTEM_OPTIONS']['path_to_gpg']);
     38         }
     39     $use_proc_open = $GLOBALS['GPG_SYSTEM_OPTIONS']['use_proc_open'];

Thus, I changed this option in this file to be false.

As a result …

Now, the entire signature validation procedure is done using exec command, avoiding pipes. This way, the total amount of time of doing so has decreased, considerably, as shown below:

Validating a GPG signature avoiding pipes

Validating a GPG signature avoiding pipes