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-2020 The SquirrelMail Project Team
  13.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  14.  * @version $Id: auth.php 14845 2020-01-07 08:09:34Z 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.     global $currentHookName;
  96.     if ($currentHookName == 'login_verified'global $key;
  97.  
  98.     sqgetGlobalVar('key',         $key,       SQ_COOKIE);
  99.     sqgetGlobalVar('onetimepad',  $onetimepad,SQ_SESSION);
  100.  
  101.     return OneTimePadDecrypt($key$onetimepad);
  102. }
  103.  
  104. /**
  105.  * Saves or updates user password information
  106.  *
  107.  * This function is used to update the password information that
  108.  * SquirrelMail stores in the existing PHP session. It does NOT
  109.  * modify the password stored in the authentication system used
  110.  * by the IMAP server.
  111.  *
  112.  * This function must be called before any html output is started.
  113.  * Direct access to password information is deprecated. The saved
  114.  * password information is available only to the SquirrelMail script
  115.  * that is called/executed AFTER the current one. If your script
  116.  * needs access to the saved password after a sqauth_save_password()
  117.  * call, use the returned OTP encrypted key.
  118.  *
  119.  * @param string $pass password
  120.  *
  121.  * @return string Password encrypted with OTP. In case the script
  122.  *                 wants to access the password information before
  123.  *                 the end of its execution.
  124.  *
  125.  * @since 1.5.1
  126.  *
  127.  */
  128. function sqauth_save_password($pass{
  129.     sqgetGlobalVar('base_uri',    $base_uri,   SQ_SESSION);
  130.  
  131.     $onetimepad OneTimePadCreate(strlen($pass));
  132.     sqsession_register($onetimepad,'onetimepad');
  133.     $key OneTimePadEncrypt($pass$onetimepad);
  134.     sqsetcookie('key'$keyfalse$base_uri);
  135.     return $key;
  136. }
  137.  
  138. /**
  139.  * Given the challenge from the server, supply the response using cram-md5 (See
  140.  * RFC 2195 for details)
  141.  *
  142.  * @param string $username User ID
  143.  * @param string $password User password supplied by User
  144.  * @param string $challenge The challenge supplied by the server
  145.  * @return string The response to be sent to the IMAP server
  146.  * @since 1.4.0
  147.  */
  148. function cram_md5_response ($username,$password,$challenge{
  149.     $challenge=base64_decode($challenge);
  150.     $hash=bin2hex(hmac_md5($challenge,$password));
  151.     $response=base64_encode($username " " $hash"\r\n";
  152.     return $response;
  153. }
  154.  
  155. /**
  156.  * Return Digest-MD5 response.
  157.  * Given the challenge from the server, calculate and return the
  158.  * response-string for digest-md5 authentication.  (See RFC 2831 for more
  159.  * details)
  160.  *
  161.  * @param string $username User ID
  162.  * @param string $password User password supplied by User
  163.  * @param string $challenge The challenge supplied by the server
  164.  * @param string $service The service name, usually 'imap'; it is used to
  165.  *    define the digest-uri.
  166.  * @param string $host The host name, usually the server's FQDN; it is used to
  167.  *    define the digest-uri.
  168.  * @param string $authz Authorization ID (since 1.5.2)
  169.  * @return string The response to be sent to the IMAP server
  170.  * @since 1.4.0
  171.  */
  172. function digest_md5_response ($username,$password,$challenge,$service,$host,$authz=''{
  173.     $result=digest_md5_parse_challenge($challenge);
  174.     //FIXME we should check that $result contains the expected values that we use below
  175.  
  176.     // verify server supports qop=auth
  177.     // $qop = explode(",",$result['qop']);
  178.     //if (!in_array("auth",$qop)) {
  179.     // rfc2831: client MUST fail if no qop methods supported
  180.     // return false;
  181.     //}
  182.     $cnonce base64_encode(bin2hex(hmac_md5(microtime())));
  183.     $ncount "00000001";
  184.  
  185.     /* This can be auth (authentication only), auth-int (integrity protection), or
  186.        auth-conf (confidentiality protection).  Right now only auth is supported.
  187.        DO NOT CHANGE THIS VALUE */
  188.     $qop_value "auth";
  189.  
  190.     $digest_uri_value $service '/' $host;
  191.  
  192.     // build the $response_value
  193.     //FIXME This will probably break badly if a server sends more than one realm
  194.     $string_a1 utf8_encode($username).":";
  195.     $string_a1 .= utf8_encode($result['realm']).":";
  196.     $string_a1 .= utf8_encode($password);
  197.     $string_a1 hmac_md5($string_a1);
  198.     $A1 $string_a1 ":" $result['nonce'":" $cnonce;
  199.     if(!empty($authz)) {
  200.         $A1 .= ":" utf8_encode($authz);
  201.     }
  202.     $A1 bin2hex(hmac_md5($A1));
  203.     $A2 "AUTHENTICATE:$digest_uri_value";
  204.     // If qop is auth-int or auth-conf, A2 gets a little extra
  205.     if ($qop_value != 'auth'{
  206.         $A2 .= ':00000000000000000000000000000000';
  207.     }
  208.     $A2 bin2hex(hmac_md5($A2));
  209.  
  210.     $string_response $result['nonce'':' $ncount ':' $cnonce ':' $qop_value;
  211.     $response_value bin2hex(hmac_md5($A1.":".$string_response.":".$A2));
  212.  
  213.     $reply 'charset=utf-8,username="' $username '",realm="' $result["realm"'",';
  214.     $reply .= 'nonce="' $result['nonce''",nc=' $ncount ',cnonce="' $cnonce '",';
  215.     $reply .= "digest-uri=\"$digest_uri_value\",response=$response_value";
  216.     $reply .= ',qop=' $qop_value;
  217.     if(!empty($authz)) {
  218.         $reply .= ',authzid=' $authz;
  219.     }
  220.     $reply base64_encode($reply);
  221.     return $reply "\r\n";
  222.  
  223. }
  224.  
  225. /**
  226.  * Parse Digest-MD5 challenge.
  227.  * This function parses the challenge sent during DIGEST-MD5 authentication and
  228.  * returns an array. See the RFC for details on what's in the challenge string.
  229.  *
  230.  * @param string $challenge Digest-MD5 Challenge
  231.  * @return array Digest-MD5 challenge decoded data
  232.  * @since 1.4.0
  233.  */
  234. function digest_md5_parse_challenge($challenge{
  235.     $challenge=base64_decode($challenge);
  236.     $parsed array();
  237.     while (!empty($challenge)) {
  238.         if ($challenge{0== ','// First char is a comma, must not be 1st time through loop
  239.             $challenge=substr($challenge,1);
  240.         }
  241.         $key=explode('=',$challenge,2);
  242.         $challenge=$key[1];
  243.         $key=$key[0];
  244.         if ($challenge{0== '"'{
  245.             // We're in a quoted value
  246.             // Drop the first quote, since we don't care about it
  247.             $challenge=substr($challenge,1);
  248.             // Now explode() to the next quote, which is the end of our value
  249.             $val=explode('"',$challenge,2);
  250.             $challenge=$val[1]// The rest of the challenge, work on it in next iteration of loop
  251.             $value=explode(',',$val[0]);
  252.             // Now, for those quoted values that are only 1 piece..
  253.             if (sizeof($value== 1{
  254.                 $value=$value[0];  // Convert to non-array
  255.             }
  256.         else {
  257.             // We're in a "simple" value - explode to next comma
  258.             $val=explode(',',$challenge,2);
  259.             if (isset($val[1])) {
  260.                 $challenge=$val[1];
  261.             else {
  262.                 unset($challenge);
  263.             }
  264.             $value=$val[0];
  265.         }
  266.         $parsed["$key"]=$value;
  267.     // End of while loop
  268.     return $parsed;
  269. }
  270.  
  271. /**
  272.   * Creates a HMAC digest that can be used for authentication purposes
  273.   * See RFCs 2104, 2617, 2831
  274.   *
  275.   * Uses PHP's Hash extension if available (enabled by default in PHP
  276.   * 5.1.2+ - see http://www.php.net/manual/en/hash.requirements.php
  277.   * or, if installed on earlier PHP versions, the PECL hash module -
  278.   * see http://pecl.php.net/package/hash
  279.   *
  280.   * Otherwise, will attempt to use the Mhash extension - see
  281.   * http://www.php.net/manual/en/mhash.requirements.php
  282.   *
  283.   * Finally, a fall-back custom implementation is used if none of
  284.   * the above are available.
  285.   *
  286.   * @param string $data The data to be encoded/hashed
  287.   * @param string $key The (shared) secret key that will be used
  288.   *                     to build the keyed hash.  This argument is
  289.   *                     technically optional, but only for internal
  290.   *                     use (when the custom hash implementation is
  291.   *                     being used) - external callers should always
  292.   *                     specify a value for this argument.
  293.   *
  294.   * @return string The HMAC-MD5 digest string
  295.   * @since 1.4.0
  296.   *
  297.   */
  298. function hmac_md5($data$key=''{
  299.  
  300.     // use PHP's native Hash extension if possible
  301.     //
  302.     if (function_exists('hash_hmac'))
  303.         return pack('H*'hash_hmac('md5'$data$key));
  304.  
  305.  
  306.     // otherwise, use (obsolete) mhash extension if available
  307.     //
  308.     if (extension_loaded('mhash')) {
  309.  
  310.         if ($key == '')
  311.             $mhash mhash(MHASH_MD5$data);
  312.         else
  313.             $mhash mhash(MHASH_MD5$data$key);
  314.  
  315.         return $mhash;
  316.     }
  317.  
  318.  
  319.     // or, our own implementation...
  320.     //
  321.     if (!$key)
  322.         return pack('H*'md5($data));
  323.  
  324.     $key str_pad($key64chr(0x00));
  325.  
  326.     if (strlen($key64)
  327.         $key pack("H*"md5($key));
  328.  
  329.     $k_ipad $key str_repeat(chr(0x36)64);
  330.     $k_opad $key str_repeat(chr(0x5c)64);
  331.  
  332.     $hmac hmac_md5($k_opad pack('H*'md5($k_ipad $data)));
  333.  
  334.     return $hmac;
  335.  
  336. }
  337.  
  338. /**
  339.  * Fillin user and password based on SMTP auth settings.
  340.  *
  341.  * @param string $user Reference to SMTP username
  342.  * @param string $pass Reference to SMTP password (unencrypted)
  343.  * @since 1.4.11
  344.  */
  345. function get_smtp_user(&$user&$pass{
  346.     global $username$smtp_auth_mech,
  347.            $smtp_sitewide_user$smtp_sitewide_pass;
  348.  
  349.     if ($smtp_auth_mech == 'none'{
  350.         $user '';
  351.         $pass '';
  352.     elseif isset($smtp_sitewide_user&& isset($smtp_sitewide_pass&&
  353.                !empty($smtp_sitewide_user)) {
  354.         $user $smtp_sitewide_user;
  355.         $pass $smtp_sitewide_pass;
  356.     else {
  357.         $user $username;
  358.         $pass sqauth_read_password();
  359.     }
  360.  
  361.     // plugin authors note: override $user or $pass by
  362.     // directly changing the arguments array contents 
  363.     // in your plugin e.g., $args[0] = 'new_username';
  364.     //
  365.     // NOTE: there is another hook in class/deliver/Deliver_SMTP.class.php
  366.     // called "smtp_authenticate" that allows a plugin to run its own
  367.     // custom authentication routine - this hook here is thus slightly
  368.     // mis-named but is too old to change.  Be careful that you do not
  369.     // confuse your hook names.
  370.     //
  371.     $temp array(&$user&$pass);
  372.     do_hook('smtp_auth'$temp);
  373. }

Documentation generated on Mon, 13 Jan 2020 04:22:01 +0100 by phpDocumentor 1.4.3