From 432db2fc4af6edc726e9a52f023cd3bd1d664667 Mon Sep 17 00:00:00 2001 From: tokul Date: Thu, 12 May 2005 08:15:19 +0000 Subject: [PATCH] replacing skip_SM_header with two different options. If admins want to shoot their own foot, they have to do that themselves and remove headers completely by modifying SquirrelMail scripts. git-svn-id: https://svn.code.sf.net/p/squirrelmail/code/trunk/squirrelmail@9411 7612ce4b-ef26-0410-bec9-ea0150e637f0 --- ChangeLog | 6 ++ class/deliver/Deliver.class.php | 104 +++++++++++++++++++++++++++++--- config/conf.pl | 84 ++++++++++++++++++++------ config/config_default.php | 39 +++++++++--- contrib/decrypt_headers.php | 104 ++++++++++++++++++++++++++++++++ functions/strings.php | 20 ++++++ 6 files changed, 319 insertions(+), 38 deletions(-) create mode 100644 contrib/decrypt_headers.php diff --git a/ChangeLog b/ChangeLog index 12c1277b..42cae76f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -342,6 +342,12 @@ Version 1.5.1 -- CVS Fixed possible php script errors caused by $SQSPELL_APP configuration variable changes. Removed $SQSPELL_EREG configuration option. Plugin's version increased to 0.5. + - $skip_SM_header option was replaced with $encode_header_key and + $hide_auth_header options. First option allows to encode user's information + with provided encryption key (set in 2. Server settings -> B. Update SMTP / + Sendmail settings). Second option allows to disable authenticated user part + in Received: header, when user can't force used email address. It is set in + 4. General Options -> 9. Allow editing of identity. Version 1.5.0 - 2 February 2004 ------------------------------- diff --git a/class/deliver/Deliver.class.php b/class/deliver/Deliver.class.php index c732bea5..9cf091aa 100644 --- a/class/deliver/Deliver.class.php +++ b/class/deliver/Deliver.class.php @@ -373,7 +373,7 @@ class Deliver { * @return string $header */ function prepareRFC822_Header($rfc822_header, $reply_rfc822_header, &$raw_length) { - global $domain, $version, $username, $skip_SM_header; + global $domain, $version, $username, $encode_header_key, $edit_identity, $hide_auth_header; /* if server var SERVER_NAME not available, use $domain */ if(!sqGetGlobalVar('SERVER_NAME', $SERVER_NAME, SQ_SERVER)) { @@ -391,8 +391,14 @@ class Deliver { /* This creates an RFC 822 date */ $date = date('D, j M Y H:i:s ', mktime()) . $this->timezone(); /* Create a message-id */ - $message_id = '<' . $REMOTE_PORT . '.' . $REMOTE_ADDR . '.'; - $message_id .= time() . '.squirrel@' . $SERVER_NAME .'>'; + $message_id = '<' . $REMOTE_PORT . '.'; + if (isset($encode_header_key) && trim($encode_header_key)!='') { + // use encrypted form of remote address + $message_id.= OneTimePadEncrypt($this->ip2hex($REMOTE_ADDR),base64_encode($encode_header_key)); + } else { + $message_id.= $REMOTE_ADDR; + } + $message_id .= '.' . time() . '.squirrel@' . $SERVER_NAME .'>'; /* Make an RFC822 Received: line */ if (isset($REMOTE_HOST)) { $received_from = "$REMOTE_HOST ([$REMOTE_ADDR])"; @@ -406,13 +412,32 @@ class Deliver { $received_from .= " (proxying for $HTTP_X_FORWARDED_FOR)"; } $header = array(); - if ( !isset($skip_SM_header) || !$skip_SM_header ) - { - $header[] = "Received: from $received_from" . $rn; - $header[] = " (SquirrelMail authenticated user $username)" . $rn; - $header[] = " by $SERVER_NAME with HTTP;" . $rn; - $header[] = " $date" . $rn; + + /** + * SquirrelMail header + * + * This Received: header provides information that allows to track + * user and machine that was used to send email. Don't remove it + * unless you understand all possible forging issues or your + * webmail installation does not prevent changes in user's email address. + * See SquirrelMail bug tracker #847107 for more details about it. + */ + if (isset($encode_header_key) && + trim($encode_header_key)!='') { + // use encoded headers, if encryption key is set and not empty + $header[].= 'X-Squirrel-UserHash: '.OneTimePadEncrypt($username,base64_encode($encode_header_key)).$rn; + $header[].= 'X-Squirrel-FromHash: '.OneTimePadEncrypt($this->ip2hex($REMOTE_ADDR),base64_encode($encode_header_key)).$rn; + if (isset($HTTP_X_FORWARDED_FOR)) + $header[].= 'X-Squirrel-ProxyHash:'.OneTimePadEncrypt($this->ip2hex($HTTP_X_FORWARDED_FOR),base64_encode($encode_header_key)).$rn; + } else { + // use default received headers + $header[] = "Received: from $received_from" . $rn; + if ($edit_identity || ! isset($hide_auth_header) || ! $hide_auth_header) + $header[] = " (SquirrelMail authenticated user $username)" . $rn; + $header[] = " by $SERVER_NAME with HTTP;" . $rn; + $header[] = " $date" . $rn; } + /* Insert the rest of the header fields */ $header[] = 'Message-ID: '. $message_id . $rn; if ($reply_rfc822_header->message_id) { @@ -697,5 +722,66 @@ class Deliver { trim($refer); return $refer; } + + /** + * Converts ip address to hexadecimal string + * + * Function is used to convert ipv4 and ipv6 addresses to hex strings. + * It removes all delimiter symbols from ip addresses, converts decimal + * ipv4 numbers to hex and pads strings in order to present full length + * address. ipv4 addresses are represented as 8 byte strings, ipv6 addresses + * are represented as 32 byte string. + * + * If function fails to detect address format, it returns unprocessed string. + * @param string $string ip address string + * @return string processed ip address string + * @since 1.5.1 + */ + function ip2hex($string) { + if (preg_match("/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/",$string,$match)) { + // ipv4 address + $ret = str_pad(dechex($match[1]),2,'0',STR_PAD_LEFT) + . str_pad(dechex($match[2]),2,'0',STR_PAD_LEFT) + . str_pad(dechex($match[3]),2,'0',STR_PAD_LEFT) + . str_pad(dechex($match[4]),2,'0',STR_PAD_LEFT); + } elseif (preg_match("/^([0-9a-h]+)\:([0-9a-h]+)\:([0-9a-h]+)\:([0-9a-h]+)\:([0-9a-h]+)\:([0-9a-h]+)\:([0-9a-h]+)\:([0-9a-h]+)$/i",$string,$match)) { + // full ipv6 address + $ret = str_pad($match[1],4,'0',STR_PAD_LEFT) + . str_pad($match[2],4,'0',STR_PAD_LEFT) + . str_pad($match[3],4,'0',STR_PAD_LEFT) + . str_pad($match[4],4,'0',STR_PAD_LEFT) + . str_pad($match[5],4,'0',STR_PAD_LEFT) + . str_pad($match[6],4,'0',STR_PAD_LEFT) + . str_pad($match[7],4,'0',STR_PAD_LEFT) + . str_pad($match[8],4,'0',STR_PAD_LEFT); + } elseif (preg_match("/^\:\:([0-9a-h\:]+)$/i",$string,$match)) { + // short ipv6 with all starting symbols nulled + $aAddr=explode(':',$match[1]); + $ret=''; + foreach ($aAddr as $addr) { + $ret.=str_pad($addr,4,'0',STR_PAD_LEFT); + } + $ret=str_pad($ret,32,'0',STR_PAD_LEFT); + } elseif (preg_match("/^([0-9a-h\:]+)::([0-9a-h\:]+)$/i",$string,$match)) { + // short ipv6 with middle part nulled + $aStart=explode(':',$match[1]); + $sStart=''; + foreach($aStart as $addr) { + $sStart.=str_pad($addr,4,'0',STR_PAD_LEFT); + } + $aEnd = explode(':',$match[2]); + $sEnd=''; + foreach($aEnd as $addr) { + $sEnd.=str_pad($addr,4,'0',STR_PAD_LEFT); + } + $ret = $sStart + . str_pad('',(32 - strlen($sStart . $sEnd)),'0',STR_PAD_LEFT) + . $sEnd; + } else { + // unknown addressing + $ret = $string; + } + return $ret; + } } ?> \ No newline at end of file diff --git a/config/conf.pl b/config/conf.pl index efb04078..fb53a0e2 100755 --- a/config/conf.pl +++ b/config/conf.pl @@ -337,6 +337,8 @@ $addrbook_global_listing = 'false' if ( !$addrbook_global_listing ); $abook_global_file = '' if ( !$abook_global_file); $abook_global_file_writeable = 'false' if ( !$abook_global_file_writeable); $abook_global_file_listing = 'true' if ( !$abook_global_file_listing ); +$encode_header_key = '' if ( !$encode_header_key ); +$hide_auth_header = 'false' if ( !$hide_auth_header ); if ( $ARGV[0] eq '--install-plugin' ) { print "Activating plugin " . $ARGV[1] . "\n"; @@ -430,7 +432,7 @@ while ( ( $command ne "q" ) && ( $command ne "Q" ) && ( $command ne ":q" ) ) { if ( lc($useSendmail) eq 'true' ) { print $WHT . "Sendmail" . $NRM . "\n--------\n"; print "4. Sendmail Path : $WHT$sendmail_path$NRM\n"; - print "5. Suppress SM header : $WHT$skip_SM_header$NRM\n"; + print "5. Header encryption key : $WHT$encode_header_key$NRM\n"; print "\n"; } else { print $WHT . "SMTP Settings" . $NRM . "\n-------------\n"; @@ -439,7 +441,7 @@ while ( ( $command ne "q" ) && ( $command ne "Q" ) && ( $command ne ":q" ) ) { print "6. POP before SMTP : $WHT$pop_before_smtp$NRM\n"; print "7. SMTP Authentication : $WHT$smtp_auth_mech$NRM\n"; print "8. Secure SMTP (TLS) : $WHT$use_smtp_tls$NRM\n"; - print "9. Suppress SM header : $WHT$skip_SM_header$NRM\n"; + print "9. Header encryption key : $WHT$encode_header_key$NRM\n"; print "\n"; } } @@ -500,7 +502,9 @@ while ( ( $command ne "q" ) && ( $command ne "Q" ) && ( $command ne ":q" ) ) { print "6. Allow use of priority : $WHT$default_use_priority$NRM\n"; print "7. Hide SM attributions : $WHT$hide_sm_attributions$NRM\n"; print "8. Allow use of receipts : $WHT$default_use_mdn$NRM\n"; - print "9. Allow editing of identity : $WHT$edit_identity$NRM/$WHT$edit_name$NRM\n"; + print "9. Allow editing of identity : $WHT$edit_identity$NRM\n"; + print " Allow editing of name : $WHT$edit_name$NRM\n"; + print " Remove username from header : $WHT$hide_auth_header$NRM\n"; print "10. Allow server thread sort : $WHT$allow_thread_sort$NRM\n"; print "11. Allow server-side sorting : $WHT$allow_server_sort$NRM\n"; print "12. Allow server charset search : $WHT$allow_charset_search$NRM\n"; @@ -692,14 +696,15 @@ while ( ( $command ne "q" ) && ( $command ne "Q" ) && ( $command ne ":q" ) ) { elsif ( $command == 8 ) { $imap_server_type = command19(); } elsif ( $command == 9 ) { $optional_delimiter = command111(); } } elsif ( $show_smtp_settings && lc($useSendmail) eq 'true' ) { - if ( $command == 4 ) { $sendmail_path = command15(); } + if ( $command == 4 ) { $sendmail_path = command15(); } + elsif ( $command == 5 ) { $encode_header_key = command114(); } } elsif ( $show_smtp_settings ) { if ( $command == 4 ) { $smtpServerAddress = command16(); } elsif ( $command == 5 ) { $smtpPort = command17(); } elsif ( $command == 6 ) { $pop_before_smtp = command18a(); } elsif ( $command == 7 ) { $smtp_auth_mech = command112b(); } elsif ( $command == 8 ) { $use_smtp_tls = command113("SMTP",$use_smtp_tls); } - elsif ( $command == 9 ) { $skip_SM_header = command114(); } + elsif ( $command == 9 ) { $encode_header_key = command114(); } } } elsif ( $menu == 3 ) { if ( $command == 1 ) { $default_folder_prefix = command21(); } @@ -1358,20 +1363,28 @@ sub command113 { return $default_val; } +# $encode_header_key sub command114{ - print "\nUse this to suppress insertion of SquirrelMail Received: headers\n"; - print "in outbound messages.\n\n"; - - $YesNo = 'n'; - $YesNo = 'y' if ( lc($skip_SM_header) eq 'true' ); - - print "Suppress SM header (y/n) [$WHT$YesNo$NRM]: $WHT"; - $new_skip_SM_header = ; - chomp($new_skip_SM_header); - - return 'true' if ( lc($new_skip_SM_header) eq 'y' ); - return 'false' if ( lc($new_skip_SM_header) eq 'n' ); - return $skip_SM_header; + print "Encryption key allows to hide SquirrelMail Received: headers\n"; + print "in outbound messages. Interface uses encryption key to encode\n"; + print "username, remote address and proxied address, then stores encoded\n"; + print "information in X-Squirrel-* headers.\n"; + print "\n"; + print "Warning: used encryption function is not bulletproof. When used\n"; + print "with static encryption keys, it provides only minimal security\n"; + print "measures and information can be decoded quickly.\n"; + print "\n"; + print "Encoded information can be decoded with decrypt_headers.php script\n"; + print "from SquirrelMail contrib/ directory.\n"; + print "\n"; + print "Enter encryption key: "; + $new_encode_header_key = ; + if ( $new_encode_header_key eq "\n" ) { + $new_encode_header_key = $encode_header_key; + } else { + $new_encode_header_key =~ s/[\r\n]//g; + } + return $new_encode_header_key; } # MOTD @@ -2079,6 +2092,7 @@ sub command39 { return 'false'; } + sub command310 { print "This allows you to prevent the editing of the user's name and "; print "email address. This is mainly useful when used with the "; @@ -2095,9 +2109,11 @@ sub command310 { if ( ( $new_edit =~ /^y\n/i ) || ( ( $new_edit =~ /^\n/ ) && ( $default_value eq "y" ) ) ) { $edit_identity = 'true'; $edit_name = 'true'; + $hide_auth_header = 'false'; } else { $edit_identity = 'false'; $edit_name = command311(); + $hide_auth_header = command311b(); } return $edit_identity; } @@ -2123,6 +2139,32 @@ sub command311 { return $edit_name; } +sub command311b { + print "SquirrelMail adds username information to every sent email."; + print "It is done in order to prevent possible sender forging when "; + print "end users are allowed to change their email and name "; + print "information.\n"; + print "\n"; + print "You can disable this header, if you think that it violates "; + print "user's privacy or security. Please note, that setting will "; + print "work only when users are not allowed to change their identity.\n"; + print "\n"; + + if ( lc($hide_auth_header) eq "true" ) { + $default_value = "y"; + } else { + $default_value = "n"; + } + print "Remove username from email headers? (y/n) [$WHT$default_value$NRM]: $WHT"; + $new_header = ; + if ( ( $new_header =~ /^y\n/i ) || ( ( $new_header =~ /^\n/ ) && ( $default_value eq "y" ) ) ) { + $hide_auth_header = "true"; + } else { + $hide_auth_header = "false"; + } + return $edit_name; +} + sub command312 { print "This option allows you to choose if users can use thread sorting\n"; print "Your IMAP server must support the THREAD command for this to work\n"; @@ -3255,8 +3297,8 @@ sub save_data { print CF "\$invert_time = $invert_time;\n"; # string print CF "\$optional_delimiter = '$optional_delimiter';\n"; - #boolean - print CF "\$skip_SM_header = $skip_SM_header;\n"; + # string + print CF "\$encode_header_key = '$encode_header_key';\n"; print CF "\n"; # string @@ -3318,6 +3360,8 @@ sub save_data { print CF "\$edit_identity = $edit_identity;\n"; # boolean print CF "\$edit_name = $edit_name;\n"; + # boolean + print CF "\$hide_auth_header = $hide_auth_header;\n"; # boolean print CF "\$allow_thread_sort = $allow_thread_sort;\n"; # boolean diff --git a/config/config_default.php b/config/config_default.php index 8d24c2e7..068c5670 100644 --- a/config/config_default.php +++ b/config/config_default.php @@ -143,18 +143,23 @@ $smtpServerAddress = 'localhost'; $smtpPort = 25; /** - * SquirrelMail header control + * SquirrelMail header encryption * - * Option can be used to disable Received: headers added by SquirrelMail. - * This can increase user's privacy and solve problems with spam filters - * that increase spam marks for dynamic dialup addresses. + * Encryption key allows to hide SquirrelMail Received: headers + * in outbound messages. Interface uses encryption key to encode + * username, remote address and proxied address, then stores encoded + * information in X-Squirrel-* headers. * - * If admin enables this setting, system should have some logging facility - * or other tools to control users. SquirrelMail's Received: header provides - * information, that can't be forged by webmail user. - * @global bool $skip_SM_header + * Warning: used encryption function is not bulletproof. When used + * with static encryption keys, it provides only minimal security + * measures and information can be decoded quickly. + * + * Encoded information can be decoded with decrypt_headers.php script + * from SquirrelMail contrib/ directory. + * @global string $encode_header_key + * @since 1.5.1 */ -$skip_SM_header = false; +$encode_header_key = ''; /** * Path to Sendmail @@ -194,6 +199,7 @@ $imapPort = 143; * macosx * hmailserver * mercury32 + * dovecot * other * * Please note that this changes only some of server settings. @@ -525,6 +531,21 @@ $default_use_mdn = true; $edit_identity = true; $edit_name = true; +/** + * SquirrelMail adds username information to every sent email. + * It is done in order to prevent possible sender forging when + * end users are allowed to change their email and name + * information. + * + * You can disable this header, if you think that it violates + * user's privacy or security. Please note, that setting will + * work only when users are not allowed to change their identity. + * + * See SquirrelMail bug tracker #847107 for more details about it. + * @global bool $hide_auth_header + */ +$hide_auth_header = false; + /** * Server Side Threading Control * diff --git a/contrib/decrypt_headers.php b/contrib/decrypt_headers.php new file mode 100644 index 00000000..1e381073 --- /dev/null +++ b/contrib/decrypt_headers.php @@ -0,0 +1,104 @@ +' + ."\n\n\n" + .""; + +if (sqgetGlobalVar('submit',$submit,SQ_POST)) { + if (! sqgetGlobalVar('secret',$secret,SQ_POST) || + empty($secret)) + echo "

You must enter encryption key.

\n"; + if (! sqgetGlobalVar('enc_string',$enc_string,SQ_POST) || + empty($enc_string)) + echo "

You must enter encrypted string.

\n"; + + if (isset($enc_string) && ! base64_decode($enc_string)) { + echo "

Encrypted string should be BASE64 encoded.
\n" + ."Please enter all characters that are listed after header name.

\n"; + } elseif (isset($secret)) { + $string=OneTimePadDecrypt($enc_string,base64_encode($secret)); + + if (sqgetGlobalVar('ip_addr',$is_addr,SQ_POST)) { + $string=hex2ip($string); + } + echo "

Decoded string: ".$string."

\n"; + } + echo "
"; +} +?> +
+

+Secret key:
+Encrypted string:
+Check, if it is an address string:
+ +

+
+ \ No newline at end of file diff --git a/functions/strings.php b/functions/strings.php index de9ae871..dca58dec 100644 --- a/functions/strings.php +++ b/functions/strings.php @@ -588,6 +588,16 @@ function get_location () { */ function OneTimePadEncrypt ($string, $epad) { $pad = base64_decode($epad); + + if (strlen($pad)>0) { + // make sure that pad is longer than string + while (strlen($string)>strlen($pad)) { + $pad.=$pad; + } + } else { + // FIXME: what should we do when $epad is not base64 encoded or empty. + } + $encrypted = ''; for ($i = 0; $i < strlen ($string); $i++) { $encrypted .= chr (ord($string[$i]) ^ ord($pad[$i])); @@ -608,6 +618,16 @@ function OneTimePadEncrypt ($string, $epad) { */ function OneTimePadDecrypt ($string, $epad) { $pad = base64_decode($epad); + + if (strlen($pad)>0) { + // make sure that pad is longer than string + while (strlen($string)>strlen($pad)) { + $pad.=$pad; + } + } else { + // FIXME: what should we do when $epad is not base64 encoded or empty. + } + $encrypted = base64_decode ($string); $decrypted = ''; for ($i = 0; $i < strlen ($encrypted); $i++) { -- 2.25.1