Source for file auth.php

Documentation is available at auth.php

  1. <?php
  2.  
  3. /**
  4.  * auth.php
  5.  *
  6.  * Contains functions used to do authentication.
  7.  * 
  8.  * Dependencies:
  9.  *  functions/global.php
  10.  *  functions/strings.php.
  11.  *
  12.  * @copyright 1999-2010 The SquirrelMail Project Team
  13.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  14.  * @version $Id: auth.php 13894 2010-01-25 03:23:30Z pdontthink $
  15.  * @package squirrelmail
  16.  */
  17.  
  18.  
  19. /**
  20.  * Detect whether user is logged in
  21.  *
  22.  * Function is similar to is_logged_in() function. If user is logged in, function
  23.  * returns true. If user is not logged in or session is expired, function saves $_POST
  24.  * and PAGE_NAME in session and returns false. POST information is saved in
  25.  * 'session_expired_post' variable, PAGE_NAME is saved in 'session_expired_location'.
  26.  *
  27.  * This function optionally checks the referrer of this page request.  If the
  28.  * administrator wants to impose a check that the referrer of this page request
  29.  * is another page on the same domain (otherwise, the page request is likely
  30.  * the result of a XSS or phishing attack), then they need to specify the
  31.  * acceptable referrer domain in a variable named $check_referrer in
  32.  * config/config.php (or the configuration tool) for which the value is
  33.  * usually the same as the $domain setting (for example:
  34.  *    $check_referrer = 'example.com';
  35.  * However, in some cases (where proxy servers are in use, etc.), the
  36.  * acceptable referrer might be different.  If $check_referrer is set to
  37.  * "###DOMAIN###", then the current value of $domain is used (useful in
  38.  * situations where $domain might change at runtime (when using the Login
  39.  * Manager plugin to host multiple domains with one SquirrelMail installation,
  40.  * for example)):
  41.  *    $check_referrer = '###DOMAIN###';
  42.  * NOTE HOWEVER, that referrer checks are not foolproof - they can be spoofed
  43.  * by browsers, and some browsers intentionally don't send them, in which
  44.  * case SquirrelMail silently ignores referrer checks.
  45.  *
  46.  * Script that uses this function instead of is_logged_in() function, must handle user
  47.  * level messages.
  48.  * @return boolean 
  49.  * @since 1.5.1
  50.  */
  51. function sqauth_is_logged_in({
  52.  
  53.     global $check_referrer$domain;
  54.     if (!sqgetGlobalVar('HTTP_REFERER'$referrerSQ_SERVER)) $referrer '';
  55.     if ($check_referrer == '###DOMAIN###'$check_referrer $domain;
  56.     if (!empty($check_referrer)) {
  57.         $ssl_check_referrer 'https://' $check_referrer;
  58.         $plain_check_referrer 'http://' $check_referrer;
  59.     }
  60.     if (sqsession_is_registered('user_is_logged_in')
  61.      && (!$check_referrer || empty($referrer)
  62.       || ($check_referrer && !empty($referrer)
  63.        && (strpos(strtolower($referrer)strtolower($plain_check_referrer)) === 0
  64.         || strpos(strtolower($referrer)strtolower($ssl_check_referrer)) === 0)))) {
  65.         return true;
  66.     }
  67.  
  68.     //  First we store some information in the new session to prevent
  69.     //  information-loss.
  70.     $session_expired_post $_POST;
  71.     if (defined('PAGE_NAME'))
  72.         $session_expired_location PAGE_NAME;
  73.     else
  74.         $session_expired_location '';
  75.  
  76.     if (!sqsession_is_registered('session_expired_post')) {
  77.         sqsession_register($session_expired_post,'session_expired_post');
  78.     }
  79.     if (!sqsession_is_registered('session_expired_location')) {
  80.         sqsession_register($session_expired_location,'session_expired_location');
  81.     }
  82.  
  83.  
  84.     return false;
  85. }
  86.  
  87. /**
  88.  * Reads and decodes stored user password information
  89.  *
  90.  * Direct access to password information is deprecated.
  91.  * @return string password in plain text
  92.  * @since 1.5.1
  93.  */
  94. function sqauth_read_password({
  95.     sqgetGlobalVar('key',         $key,       SQ_COOKIE);
  96.     sqgetGlobalVar('onetimepad',  $onetimepad,SQ_SESSION);
  97.  
  98.     return OneTimePadDecrypt($key$onetimepad);
  99. }
  100.  
  101. /**
  102.  * Saves or updates user password information
  103.  *
  104.  * This function is used to update the password information that
  105.  * SquirrelMail stores in the existing PHP session. It does NOT
  106.  * modify the password stored in the authentication system used
  107.  * by the IMAP server.
  108.  *
  109.  * This function must be called before any html output is started.
  110.  * Direct access to password information is deprecated. The saved
  111.  * password information is available only to the SquirrelMail script
  112.  * that is called/executed AFTER the current one. If your script
  113.  * needs access to the saved password after a sqauth_save_password()
  114.  * call, use the returned OTP encrypted key.
  115.  *
  116.  * @param string $pass password
  117.  *
  118.  * @return string Password encrypted with OTP. In case the script
  119.  *                 wants to access the password information before
  120.  *                 the end of its execution.
  121.  *
  122.  * @since 1.5.1
  123.  *
  124.  */
  125. function sqauth_save_password($pass{
  126.     sqgetGlobalVar('base_uri',    $base_uri,   SQ_SESSION);
  127.  
  128.     $onetimepad OneTimePadCreate(strlen($pass));
  129.     sqsession_register($onetimepad,'onetimepad');
  130.     $key OneTimePadEncrypt($pass$onetimepad);
  131.     sqsetcookie('key'$keyfalse$base_uri);
  132.     return $key;
  133. }
  134.  
  135. /**
  136.  * Given the challenge from the server, supply the response using cram-md5 (See
  137.  * RFC 2195 for details)
  138.  *
  139.  * @param string $username User ID
  140.  * @param string $password User password supplied by User
  141.  * @param string $challenge The challenge supplied by the server
  142.  * @return string The response to be sent to the IMAP server
  143.  * @since 1.4.0
  144.  */
  145. function cram_md5_response ($username,$password,$challenge{
  146.     $challenge=base64_decode($challenge);
  147.     $hash=bin2hex(hmac_md5($challenge,$password));
  148.     $response=base64_encode($username " " $hash"\r\n";
  149.     return $response;
  150. }
  151.  
  152. /**
  153.  * Return Digest-MD5 response.
  154.  * Given the challenge from the server, calculate and return the
  155.  * response-string for digest-md5 authentication.  (See RFC 2831 for more
  156.  * details)
  157.  *
  158.  * @param string $username User ID
  159.  * @param string $password User password supplied by User
  160.  * @param string $challenge The challenge supplied by the server
  161.  * @param string $service The service name, usually 'imap'; it is used to
  162.  *    define the digest-uri.
  163.  * @param string $host The host name, usually the server's FQDN; it is used to
  164.  *    define the digest-uri.
  165.  * @param string $authz Authorization ID (since 1.5.2)
  166.  * @return string The response to be sent to the IMAP server
  167.  * @since 1.4.0
  168.  */
  169. function digest_md5_response ($username,$password,$challenge,$service,$host,$authz=''{
  170.     $result=digest_md5_parse_challenge($challenge);
  171.     //FIXME we should check that $result contains the expected values that we use below
  172.  
  173.     // verify server supports qop=auth
  174.     // $qop = explode(",",$result['qop']);
  175.     //if (!in_array("auth",$qop)) {
  176.     // rfc2831: client MUST fail if no qop methods supported
  177.     // return false;
  178.     //}
  179.     $cnonce base64_encode(bin2hex(hmac_md5(microtime())));
  180.     $ncount "00000001";
  181.  
  182.     /* This can be auth (authentication only), auth-int (integrity protection), or
  183.        auth-conf (confidentiality protection).  Right now only auth is supported.
  184.        DO NOT CHANGE THIS VALUE */
  185.     $qop_value "auth";
  186.  
  187.     $digest_uri_value $service '/' $host;
  188.  
  189.     // build the $response_value
  190.     //FIXME This will probably break badly if a server sends more than one realm
  191.     $string_a1 utf8_encode($username).":";
  192.     $string_a1 .= utf8_encode($result['realm']).":";
  193.     $string_a1 .= utf8_encode($password);
  194.     $string_a1 hmac_md5($string_a1);
  195.     $A1 $string_a1 ":" $result['nonce'":" $cnonce;
  196.     if(!empty($authz)) {
  197.         $A1 .= ":" utf8_encode($authz);
  198.     }
  199.     $A1 bin2hex(hmac_md5($A1));
  200.     $A2 "AUTHENTICATE:$digest_uri_value";
  201.     // If qop is auth-int or auth-conf, A2 gets a little extra
  202.     if ($qop_value != 'auth'{
  203.         $A2 .= ':00000000000000000000000000000000';
  204.     }
  205.     $A2 bin2hex(hmac_md5($A2));
  206.  
  207.     $string_response $result['nonce'':' $ncount ':' $cnonce ':' $qop_value;
  208.     $response_value bin2hex(hmac_md5($A1.":".$string_response.":".$A2));
  209.  
  210.     $reply 'charset=utf-8,username="' $username '",realm="' $result["realm"'",';
  211.     $reply .= 'nonce="' $result['nonce''",nc=' $ncount ',cnonce="' $cnonce '",';
  212.     $reply .= "digest-uri=\"$digest_uri_value\",response=$response_value";
  213.     $reply .= ',qop=' $qop_value;
  214.     if(!empty($authz)) {
  215.         $reply .= ',authzid=' $authz;
  216.     }
  217.     $reply base64_encode($reply);
  218.     return $reply "\r\n";
  219.  
  220. }
  221.  
  222. /**
  223.  * Parse Digest-MD5 challenge.
  224.  * This function parses the challenge sent during DIGEST-MD5 authentication and
  225.  * returns an array. See the RFC for details on what's in the challenge string.
  226.  *
  227.  * @param string $challenge Digest-MD5 Challenge
  228.  * @return array Digest-MD5 challenge decoded data
  229.  * @since 1.4.0
  230.  */
  231. function digest_md5_parse_challenge($challenge{
  232.     $challenge=base64_decode($challenge);
  233.     $parsed array();
  234.     while (!empty($challenge)) {
  235.         if ($challenge{0== ','// First char is a comma, must not be 1st time through loop
  236.             $challenge=substr($challenge,1);
  237.         }
  238.         $key=explode('=',$challenge,2);
  239.         $challenge=$key[1];
  240.         $key=$key[0];
  241.         if ($challenge{0== '"'{
  242.             // We're in a quoted value
  243.             // Drop the first quote, since we don't care about it
  244.             $challenge=substr($challenge,1);
  245.             // Now explode() to the next quote, which is the end of our value
  246.             $val=explode('"',$challenge,2);
  247.             $challenge=$val[1]// The rest of the challenge, work on it in next iteration of loop
  248.             $value=explode(',',$val[0]);
  249.             // Now, for those quoted values that are only 1 piece..
  250.             if (sizeof($value== 1{
  251.                 $value=$value[0];  // Convert to non-array
  252.             }
  253.         else {
  254.             // We're in a "simple" value - explode to next comma
  255.             $val=explode(',',$challenge,2);
  256.             if (isset($val[1])) {
  257.                 $challenge=$val[1];
  258.             else {
  259.                 unset($challenge);
  260.             }
  261.             $value=$val[0];
  262.         }
  263.         $parsed["$key"]=$value;
  264.     // End of while loop
  265.     return $parsed;
  266. }
  267.  
  268. /**
  269.  * Creates a HMAC digest that can be used for auth purposes
  270.  * See RFCs 2104, 2617, 2831
  271.  * Uses mhash() extension if available
  272.  *
  273.  * @param string $data Data to apply hash function to.
  274.  * @param string $key Optional key, which, if supplied, will be used to
  275.  *  calculate data's HMAC.
  276.  * @return string HMAC Digest string
  277.  * @since 1.4.0
  278.  */
  279. function hmac_md5($data$key=''{
  280.     if (extension_loaded('mhash')) {
  281.         if ($key== ''{
  282.             $mhash=mhash(MHASH_MD5,$data);
  283.         else {
  284.             $mhash=mhash(MHASH_MD5,$data,$key);
  285.         }
  286.         return $mhash;
  287.     }
  288.     if (!$key{
  289.         return pack('H*',md5($data));
  290.     }
  291.     $key str_pad($key,64,chr(0x00));
  292.     if (strlen($key64{
  293.         $key pack("H*",md5($key));
  294.     }
  295.     $k_ipad =  $key str_repeat(chr(0x36)64;
  296.     $k_opad =  $key str_repeat(chr(0x5c)64;
  297.     /* Heh, let's get recursive. */
  298.     $hmac=hmac_md5($k_opad pack("H*",md5($k_ipad $data)) );
  299.     return $hmac;
  300. }
  301.  
  302. /**
  303.  * Fillin user and password based on SMTP auth settings.
  304.  *
  305.  * @param string $user Reference to SMTP username
  306.  * @param string $pass Reference to SMTP password (unencrypted)
  307.  * @since 1.4.11
  308.  */
  309. function get_smtp_user(&$user&$pass{
  310.     global $username$smtp_auth_mech,
  311.            $smtp_sitewide_user$smtp_sitewide_pass;
  312.  
  313.     if ($smtp_auth_mech == 'none'{
  314.         $user '';
  315.         $pass '';
  316.     elseif isset($smtp_sitewide_user&& isset($smtp_sitewide_pass&&
  317.                !empty($smtp_sitewide_user)) {
  318.         $user $smtp_sitewide_user;
  319.         $pass $smtp_sitewide_pass;
  320.     else {
  321.         $user $username;
  322.         $pass sqauth_read_password();
  323.     }
  324.  
  325.     // plugin authors note: override $user or $pass by
  326.     // directly changing the arguments array contents 
  327.     // in your plugin e.g., $args[0] = 'new_username';
  328.     //
  329.     $temp array(&$user&$pass);
  330.     do_hook('smtp_auth'$temp);
  331. }

Documentation generated on Mon, 06 Sep 2010 04:16:38 +0200 by phpDocumentor 1.4.3