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:
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: