Source for file auth.php

Documentation is available at auth.php

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

Documentation generated on 2020年1月13日 04:24:15 +0100 by phpDocumentor 1.4.3

AltStyle によって変換されたページ (->オリジナル) /