643 |
643 |
644 function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER, $captcha_hash = false, $captcha_code = false, $remember = false) |
644 function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER, $captcha_hash = false, $captcha_code = false, $remember = false) |
645 { |
645 { |
646 global $db, $session, $paths, $template, $plugins; // Common objects |
646 global $db, $session, $paths, $template, $plugins; // Common objects |
647 |
647 |
648 $pass_hashed = ( $already_md5ed ) ? $password : md5($password); |
648 if ( $already_md5ed ) |
|
649 { |
|
650 // No longer supported |
|
651 return array( |
|
652 'mode' => 'error', |
|
653 'error' => '$already_md5ed is no longer supported (set this parameter to false and make sure the password you send to $session->login_without_crypto() is not hashed)' |
|
654 ); |
|
655 } |
649 |
656 |
650 // Replace underscores with spaces in username |
657 // Replace underscores with spaces in username |
651 // (Added in 1.0.2) |
658 // (Added in 1.0.2) |
652 $username = str_replace('_', ' ', $username); |
659 $username = str_replace('_', ' ', $username); |
653 |
660 |
654 // Perhaps we're upgrading Enano? |
661 // Perhaps we're upgrading Enano? |
655 if($this->compat) |
662 if($this->compat) |
656 { |
663 { |
657 return $this->login_compat($username, $pass_hashed, $level); |
664 return $this->login_compat($username, md5($password), $level); |
658 } |
665 } |
659 |
666 |
660 if ( !defined('IN_ENANO_INSTALL') ) |
667 if ( !defined('IN_ENANO_INSTALL') ) |
661 { |
668 { |
662 // Lockout stuff |
669 // Lockout stuff |
708 |
715 |
709 // Initialize our success switch |
716 // Initialize our success switch |
710 $success = false; |
717 $success = false; |
711 |
718 |
712 // Retrieve the real password from the database |
719 // Retrieve the real password from the database |
713 $this->sql('SELECT password,old_encryption,user_id,user_level,temp_password,temp_password_time FROM '.table_prefix.'users WHERE ' . ENANO_SQLFUNC_LOWERCASE . '(username)=\''.$this->prepare_text(strtolower($username)).'\';'); |
720 $username_db = $db->escape(strtolower($username)); |
|
721 if ( !$db->sql_query('SELECT password,password_salt,old_encryption,user_id,user_level,temp_password,temp_password_time FROM '.table_prefix."users\n" |
|
722 . " WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username_db';") ) |
|
723 { |
|
724 $this->sql('SELECT password,\'\' AS password_salt,old_encryption,user_id,user_level,temp_password,temp_password_time FROM '.table_prefix."users\n" |
|
725 . " WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username_db';"); |
|
726 } |
714 if($db->numrows() < 1) |
727 if($db->numrows() < 1) |
715 { |
728 { |
716 // This wasn't logged in <1.0.2, dunno how it slipped through |
729 // This wasn't logged in <1.0.2, dunno how it slipped through |
717 if($level > USER_LEVEL_MEMBER) |
730 if($level > USER_LEVEL_MEMBER) |
718 $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.enano_date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); |
731 $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.enano_date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); |
745 |
758 |
746 // Check to see if we're logging in using a temporary password |
759 // Check to see if we're logging in using a temporary password |
747 |
760 |
748 if((intval($row['temp_password_time']) + 3600*24) > time() ) |
761 if((intval($row['temp_password_time']) + 3600*24) > time() ) |
749 { |
762 { |
750 $temp_pass = $aes->decrypt( $row['temp_password'], $this->private_key, ENC_HEX ); |
763 $temp_pass = hmac_sha1($password, $row['password_salt']); |
751 if( md5($temp_pass) == $pass_hashed ) |
764 if( $temp_pass === $row['temp_password'] ) |
752 { |
765 { |
753 $code = $plugins->setHook('login_password_reset'); |
766 $code = $plugins->setHook('login_password_reset'); |
754 foreach ( $code as $cmd ) |
767 foreach ( $code as $cmd ) |
755 { |
768 { |
756 eval($cmd); |
769 eval($cmd); |
757 } |
770 } |
758 |
771 |
759 return array( |
772 return array( |
760 'success' => false, |
773 'success' => false, |
761 'error' => 'valid_reset', |
774 'error' => 'valid_reset', |
762 'redirect_url' => makeUrlComplete('Special', 'PasswordReset/stage2/' . $row['user_id'] . '/' . $row['temp_password']) |
775 'redirect_url' => makeUrlComplete('Special', 'PasswordReset/stage2/' . $row['user_id'] . '/' . $this->pk_encrypt($password)) |
763 ); |
776 ); |
764 } |
777 } |
765 } |
778 } |
766 |
779 |
767 if($row['old_encryption'] == 1) |
780 if ( $row['old_encryption'] == 1 ) |
768 { |
781 { |
769 // The user's password is stored using the obsolete and insecure MD5 algorithm - we'll update the field with the new password |
782 // The user's password is stored using the obsolete and insecure MD5 algorithm - we'll update the field with the new password |
770 if($pass_hashed == $row['password'] && !$already_md5ed) |
783 if(md5($password) === $row['password']) |
771 { |
784 { |
772 $pass_stashed = $aes->encrypt($password, $this->private_key, ENC_HEX); |
785 $hmac_secret = AESCrypt::randkey(20); |
773 $this->sql('UPDATE '.table_prefix.'users SET password=\''.$pass_stashed.'\',old_encryption=0 WHERE user_id='.$row['user_id'].';'); |
786 $password_hmac = hmac_sha1($password, $hmac_secret); |
|
787 $this->sql('UPDATE '.table_prefix."users SET password = '$password_hmac', password_salt = '$hmac_secret', old_encryption = 0 WHERE user_id={$row['user_id']};"); |
774 $success = true; |
788 $success = true; |
775 } |
789 } |
776 elseif($pass_hashed == $row['password'] && $already_md5ed) |
790 } |
777 { |
791 else if ( $row['old_encryption'] == 2 ) |
778 // We don't have the real password so don't bother with encrypting it, just call it success and get out of here |
792 { |
|
793 // Our password field uses the 1.0RC1-1.1.5 encryption format |
|
794 $real_pass = $aes->decrypt($row['password'], $this->private_key); |
|
795 if($password === $real_pass) |
|
796 { |
779 $success = true; |
797 $success = true; |
|
798 $hmac_secret = AESCrypt::randkey(20); |
|
799 $password_hmac = hmac_sha1($password, $hmac_secret); |
|
800 $this->sql('UPDATE '.table_prefix."users SET password = '$password_hmac', password_salt = '$hmac_secret', old_encryption = 0 WHERE user_id={$row['user_id']};"); |
|
801 $success = true; |
780 } |
802 } |
781 } |
803 } |
782 else |
804 else |
783 { |
805 { |
784 // Our password field is up-to-date with the >=1.0RC1 encryption standards, so decrypt the password in the table and see if we have a match |
806 // Password uses HMAC-SHA1 |
785 $real_pass = $aes->decrypt($row['password'], $this->private_key); |
807 $user_challenge = hmac_sha1($password, $row['password_salt']); |
786 if($pass_hashed == md5($real_pass)) |
808 $password_hmac =& $row['password']; |
|
809 if ( $user_challenge === $password_hmac ) |
787 { |
810 { |
788 $success = true; |
811 $success = true; |
789 } |
812 } |
790 } |
813 } |
791 if($success) |
814 if($success) |
884 /** |
907 /** |
885 * Registers a session key in the database. This function *ASSUMES* that the username and password have already been validated! |
908 * Registers a session key in the database. This function *ASSUMES* that the username and password have already been validated! |
886 * Basically the session key is a hex-encoded cookie (encrypted with the site's private key) that says "u=[username];p=[sha1 of password];s=[unique key id]" |
909 * Basically the session key is a hex-encoded cookie (encrypted with the site's private key) that says "u=[username];p=[sha1 of password];s=[unique key id]" |
887 * @param int $user_id |
910 * @param int $user_id |
888 * @param string $username |
911 * @param string $username |
889 * @param string $password |
912 * @param string $password_hmac The HMAC of the user's password, right from the database |
890 * @param int $level The level of access to grant, defaults to USER_LEVEL_MEMBER |
913 * @param int $level The level of access to grant, defaults to USER_LEVEL_MEMBER |
891 * @param bool $remember Whether the session should be long-term (true) or not (false). Defaults to short-term. |
914 * @param bool $remember Whether the session should be long-term (true) or not (false). Defaults to short-term. |
892 * @return bool |
915 * @return bool |
893 */ |
916 */ |
894 |
917 |
895 function register_session($user_id, $username, $password, $level = USER_LEVEL_MEMBER, $remember = false) |
918 function register_session($user_id, $username, $password_hmac, $level = USER_LEVEL_MEMBER, $remember = false) |
896 { |
919 { |
897 global $db, $session, $paths, $template, $plugins; // Common objects |
920 global $db, $session, $paths, $template, $plugins; // Common objects |
898 |
921 |
899 // Random key identifier |
922 // Random key identifier |
900 $salt = md5(microtime() . mt_rand()); |
923 $salt = ''; |
901 |
924 for ( $i = 0; $i < 32; $i++ ) |
902 // SHA1 hash of password, stored in the key |
925 { |
903 $passha1 = sha1($password); |
926 $salt .= chr(mt_rand(32, 127)); |
904 |
927 } |
905 // Unencrypted session key |
928 |
906 $session_key = "u=$username;p=$passha1;s=$salt"; |
929 // Session key |
|
930 $session_key = hmac_sha1($password_hmac, $salt); |
907 |
931 |
908 // Type of key |
932 // Type of key |
909 $key_type = ( $level > USER_LEVEL_MEMBER ) ? SK_ELEV : ( $remember ? SK_LONG : SK_SHORT ); |
933 $key_type = ( $level > USER_LEVEL_MEMBER ) ? SK_ELEV : ( $remember ? SK_LONG : SK_SHORT ); |
910 |
934 |
911 // Encrypt the key |
|
912 $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE); |
|
913 $session_key = $aes->encrypt($session_key, $this->private_key, ENC_HEX); |
|
914 |
|
915 // If we're registering an elevated-privilege key, it needs to be on GET |
935 // If we're registering an elevated-privilege key, it needs to be on GET |
916 if($level > USER_LEVEL_MEMBER) |
936 if($level > USER_LEVEL_MEMBER) |
917 { |
937 { |
918 // Reverse it - cosmetic only ;-) |
938 // Reverse it - cosmetic only ;-) |
919 $hexkey = strrev($session_key); |
939 $hexkey = $session_key; |
920 $this->sid_super = $hexkey; |
940 $this->sid_super = $hexkey; |
921 $_GET['auth'] = $hexkey; |
941 $_GET['auth'] = $hexkey; |
922 } |
942 } |
923 else |
943 else |
924 { |
944 { |
941 die('Somehow an SQL injection attempt crawled into our session registrar! (1)'); |
961 die('Somehow an SQL injection attempt crawled into our session registrar! (1)'); |
942 if(!is_int($level)) |
962 if(!is_int($level)) |
943 die('Somehow an SQL injection attempt crawled into our session registrar! (2)'); |
963 die('Somehow an SQL injection attempt crawled into our session registrar! (2)'); |
944 |
964 |
945 // All done! |
965 // All done! |
946 $query = $db->sql_query('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time, key_type) VALUES(\''.$keyhash.'\', \''.$salt.'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.', ' . $key_type . ');'); |
966 $query = $db->sql_query('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time, key_type) VALUES(\''.$keyhash.'\', \''.$db->escape($salt).'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.', ' . $key_type . ');'); |
947 if ( !$query && defined('IN_ENANO_UPGRADE') ) |
967 if ( !$query && defined('IN_ENANO_UPGRADE') ) |
948 // we're trying to upgrade so the key_type column is probably missing - try it again without specifying the key type |
968 // we're trying to upgrade so the key_type column is probably missing - try it again without specifying the key type |
949 $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$keyhash.'\', \''.$salt.'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');'); |
969 $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$keyhash.'\', \''.$db->escape($salt).'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');'); |
950 |
970 |
951 return true; |
971 return true; |
952 } |
972 } |
953 |
973 |
954 /** |
974 /** |
1023 |
1043 |
1024 function validate_session($key) |
1044 function validate_session($key) |
1025 { |
1045 { |
1026 global $db, $session, $paths, $template, $plugins; // Common objects |
1046 global $db, $session, $paths, $template, $plugins; // Common objects |
1027 profiler_log("SessionManager: checking session: " . sha1($key)); |
1047 profiler_log("SessionManager: checking session: " . sha1($key)); |
|
1048 |
|
1049 if ( strlen($key) > 48 ) |
|
1050 { |
|
1051 return $this->validate_aes_session($key); |
|
1052 } |
|
1053 |
|
1054 profiler_log("SessionManager: checking session: " . $key); |
|
1055 |
|
1056 return $this->validate_session_shared($key, ''); |
|
1057 } |
|
1058 |
|
1059 /** |
|
1060 * Validates an old-format AES session key. DO NOT USE THIS. Will return false if called outside of an upgrade. |
|
1061 * @param string Session key |
|
1062 * @return array |
|
1063 */ |
|
1064 |
|
1065 protected function validate_aes_session($key) |
|
1066 { |
|
1067 global $db, $session, $paths, $template, $plugins; // Common objects |
|
1068 |
|
1069 // No valid use except during upgrades |
|
1070 if ( !preg_match('/^upg-/', enano_version()) || !defined('IN_ENANO_UPGRADE') ) |
|
1071 return false; |
|
1072 |
1028 $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE); |
1073 $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE); |
1029 $decrypted_key = $aes->decrypt($key, $this->private_key, ENC_HEX); |
1074 $decrypted_key = $aes->decrypt($key, $this->private_key, ENC_HEX); |
1030 |
|
1031 if ( !$decrypted_key ) |
1075 if ( !$decrypted_key ) |
1032 { |
1076 { |
1033 // die_semicritical('AES encryption error', '<p>Something went wrong during the AES decryption process.</p><pre>'.print_r($decrypted_key, true).'</pre>'); |
1077 // die_semicritical('AES encryption error', '<p>Something went wrong during the AES decryption process.</p><pre>'.print_r($decrypted_key, true).'</pre>'); |
1034 return false; |
1078 return false; |
1035 } |
1079 } |
1040 // echo '(debug) $session->validate_session: Key does not match regex<br />Decrypted key: '.$decrypted_key; |
1084 // echo '(debug) $session->validate_session: Key does not match regex<br />Decrypted key: '.$decrypted_key; |
1041 return false; |
1085 return false; |
1042 } |
1086 } |
1043 $keyhash = md5($key); |
1087 $keyhash = md5($key); |
1044 $salt = $db->escape($keydata[3]); |
1088 $salt = $db->escape($keydata[3]); |
1045 profiler_log("SessionManager: checking session: " . sha1($key) . ": decrypted session key to $decrypted_key"); |
1089 |
|
1090 return $this->validate_session_shared($keyhash, $salt, true); |
|
1091 } |
|
1092 |
|
1093 /** |
|
1094 * Shared portion of session validation. Do not try to call this. |
|
1095 * @return array |
|
1096 * @access private |
|
1097 */ |
|
1098 |
|
1099 protected function validate_session_shared($key, $salt, $loose_call = false) |
|
1100 { |
|
1101 global $db, $session, $paths, $template, $plugins; // Common objects |
|
1102 |
1046 // using a normal call to $db->sql_query to avoid failing on errors here |
1103 // using a normal call to $db->sql_query to avoid failing on errors here |
1047 $query = $db->sql_query('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,' . "\n" |
1104 $columns_select = "u.user_id AS uid, u.username, u.password, u.password_salt, u.email, u.real_name, u.user_level, u.theme,\n" |
1048 . ' u.reg_time,u.account_active,u.activation_key,u.user_lang,u.user_title,k.source_ip,k.time,k.auth_level,k.key_type,COUNT(p.message_id) AS num_pms,' . "\n" |
1105 . " u.style,u.signature, u.reg_time, u.account_active, u.activation_key, u.user_lang, u.user_title, k.salt, k.source_ip,\n" |
1049 . ' u.user_timezone, u.user_dst, x.* FROM '.table_prefix.'session_keys AS k' . "\n" |
1106 . " k.time, k.auth_level, k.key_type, COUNT(p.message_id) AS num_pms, u.user_timezone, u.user_dst, x.*"; |
1050 . ' LEFT JOIN '.table_prefix.'users AS u' . "\n" |
1107 |
1051 . ' ON ( u.user_id=k.user_id )' . "\n" |
1108 $columns_groupby = "u.user_id, u.username, u.password, u.email, u.real_name, u.user_level, u.theme, u.style, u.signature,\n" |
1052 . ' LEFT JOIN '.table_prefix.'users_extra AS x' . "\n" |
1109 . " u.reg_time, u.account_active, u.activation_key, u.user_lang, u.user_timezone, u.user_title, u.user_dst,\n" |
1053 . ' ON ( u.user_id=x.user_id OR x.user_id IS NULL )' . "\n" |
1110 . " k.salt, k.source_ip, k.time, k.auth_level, k.key_type, x.user_id, x.user_aim, x.user_yahoo, x.user_msn,\n" |
1054 . ' LEFT JOIN '.table_prefix.'privmsgs AS p' . "\n" |
1111 . " x.user_xmpp, x.user_homepage, x.user_location, x.user_job, x.user_hobbies, x.email_public,\n" |
1055 . ' ON ( p.message_to=u.username AND p.message_read=0 )' . "\n" |
1112 . " x.disable_js_fx"; |
1056 . ' WHERE k.session_key=\''.$keyhash.'\'' . "\n" |
1113 |
1057 . ' AND k.salt=\''.$salt.'\'' . "\n" |
1114 $joins = " LEFT JOIN " . table_prefix . "users AS u\n" |
1058 . ' GROUP BY u.user_id,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,u.user_lang,u.user_timezone,u.user_title,u.user_dst,k.source_ip,k.time,k.auth_level,k.key_type,x.user_id, x.user_aim, x.user_yahoo, x.user_msn, x.user_xmpp, x.user_homepage, x.user_location, x.user_job, x.user_hobbies, x.email_public, x.disable_js_fx;'); |
1115 . " ON ( u.user_id=k.user_id )\n" |
|
1116 . " LEFT JOIN " . table_prefix . "users_extra AS x\n" |
|
1117 . " ON ( u.user_id=x.user_id OR x.user_id IS NULL )\n" |
|
1118 . " LEFT JOIN " . table_prefix . "privmsgs AS p\n" |
|
1119 . " ON ( p.message_to=u.username AND p.message_read=0 )\n"; |
|
1120 if ( !$loose_call ) |
|
1121 { |
|
1122 $key_md5 = md5($key); |
|
1123 $query = $db->sql_query("SELECT $columns_select\n" |
|
1124 . "FROM " . table_prefix . "session_keys AS k\n" |
|
1125 . $joins |
|
1126 . " WHERE k.session_key='$key_md5'\n" |
|
1127 . " GROUP BY $columns_groupby;"); |
|
1128 } |
|
1129 else |
|
1130 { |
|
1131 $query = $db->sql_query("SELECT $columns_select\n" |
|
1132 . "FROM " . table_prefix . "session_keys AS k\n" |
|
1133 . $joins |
|
1134 . " WHERE k.session_key='$key'\n" |
|
1135 . " AND k.salt='$salt'\n" |
|
1136 . " GROUP BY $columns_groupby;"); |
|
1137 } |
1059 |
1138 |
1060 if ( !$query && ( defined('IN_ENANO_INSTALL') or defined('IN_ENANO_UPGRADE') ) ) |
1139 if ( !$query && ( defined('IN_ENANO_INSTALL') or defined('IN_ENANO_UPGRADE') ) ) |
1061 { |
1140 { |
1062 $query = $this->sql('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms, 1440 AS user_timezone, \'0;0;0;0;60\' AS user_dst, ' . SK_SHORT . ' AS key_type FROM '.table_prefix.'session_keys AS k |
1141 $query = $this->sql('SELECT u.user_id AS uid,u.username,u.password,\'\' AS password_salt,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms, 1440 AS user_timezone, \'0;0;0;0;60\' AS user_dst, ' . SK_SHORT . ' AS key_type FROM '.table_prefix.'session_keys AS k |
1063 LEFT JOIN '.table_prefix.'users AS u |
1142 LEFT JOIN '.table_prefix.'users AS u |
1064 ON ( u.user_id=k.user_id ) |
1143 ON ( u.user_id=k.user_id ) |
1065 LEFT JOIN '.table_prefix.'privmsgs AS p |
1144 LEFT JOIN '.table_prefix.'privmsgs AS p |
1066 ON ( p.message_to=u.username AND p.message_read=0 ) |
1145 ON ( p.message_to=u.username AND p.message_read=0 ) |
1067 WHERE k.session_key=\''.$keyhash.'\' |
1146 WHERE k.session_key=\''.$keyhash.'\' |
3400 * @param string The name of the field with the DiffieHellman public key |
3513 * @param string The name of the field with the DiffieHellman public key |
3401 * @param string The name of the field that the client should populate with its public key |
3514 * @param string The name of the field that the client should populate with its public key |
3402 * @return string |
3515 * @return string |
3403 */ |
3516 */ |
3404 |
3517 |
3405 function aes_javascript($form_name, $pw_field, $use_crypt, $crypt_key, $crypt_data, $challenge, $dh_supported = false, $dh_pubkey = false, $dh_client_pubkey = false) |
3518 function aes_javascript($form_name, $pw_field, $use_crypt = 'use_crypt', $crypt_key = 'crypt_key', $crypt_data = 'crypt_data', $challenge = 'challenge_data', $dh_supported = 'dh_supported', $dh_pubkey = 'dh_public_key', $dh_client_pubkey = 'dh_client_public_key') |
3406 { |
3519 { |
3407 $code = ' |
3520 $code = ' |
3408 <script type="text/javascript"> |
3521 <script type="text/javascript"> |
3409 |
3522 |
3410 function runEncryption() |
3523 function runEncryption(nowhiteout) |
3411 { |
3524 { |
3412 var frm = document.forms.'.$form_name.'; |
3525 var frm = document.forms.'.$form_name.'; |
3413 whiteOutForm(frm); |
3526 if ( !nowhiteout ) |
|
3527 whiteOutForm(frm); |
3414 |
3528 |
3415 load_component(\'crypto\'); |
3529 load_component(\'crypto\'); |
3416 var testpassed = ' . ( ( isset($_GET['use_crypt']) && $_GET['use_crypt']=='0') ? 'false; // CRYPTO-AUTH DISABLED ON USER REQUEST // ' : '' ) . '( aes_self_test() && md5_vm_test() ); |
3530 var testpassed = ' . ( ( isset($_GET['use_crypt']) && $_GET['use_crypt']=='0') ? 'false; // CRYPTO-AUTH DISABLED ON USER REQUEST // ' : '' ) . '( aes_self_test() && md5_vm_test() ); |
3417 var use_diffiehellman = false;' . "\n"; |
3531 var use_diffiehellman = false;' . "\n"; |
3418 if ( $dh_supported && $dh_pubkey ) |
3532 if ( $dh_supported && $dh_pubkey ) |
3504 } |
3637 } |
3505 cryptstring = byteArrayToHex(cryptstring); |
3638 cryptstring = byteArrayToHex(cryptstring); |
3506 frm.'.$crypt_data.'.value = cryptstring; |
3639 frm.'.$crypt_data.'.value = cryptstring; |
3507 frm.'.$pw_field.'.value = \'\'; |
3640 frm.'.$pw_field.'.value = \'\'; |
3508 } |
3641 } |
3509 return false; |
|
3510 } |
3642 } |
3511 </script> |
3643 </script> |
3512 '; |
3644 '; |
3513 return $code; |
3645 return $code; |
|
3646 } |
|
3647 |
|
3648 /** |
|
3649 * Generates the HTML form elements required for an encrypted logon experience. |
|
3650 * @return string |
|
3651 */ |
|
3652 |
|
3653 function generate_aes_form() |
|
3654 { |
|
3655 $return = '<input type="hidden" name="use_crypt" value="no" />'; |
|
3656 $return .= '<input type="hidden" name="crypt_key" value="' . $this->rijndael_genkey() . '" />'; |
|
3657 $return .= '<input type="hidden" name="crypt_data" value="" />'; |
|
3658 $return .= '<input type="hidden" name="challenge_data" value="' . $this->dss_rand() . '" />'; |
|
3659 |
|
3660 require_once(ENANO_ROOT . '/includes/math.php'); |
|
3661 require_once(ENANO_ROOT . '/includes/diffiehellman.php'); |
|
3662 |
|
3663 global $dh_supported, $_math; |
|
3664 if ( $dh_supported ) |
|
3665 { |
|
3666 $dh_key_priv = dh_gen_private(); |
|
3667 $dh_key_pub = dh_gen_public($dh_key_priv); |
|
3668 $dh_key_priv = $_math->str($dh_key_priv); |
|
3669 $dh_key_pub = $_math->str($dh_key_pub); |
|
3670 // store the keys in the DB |
|
3671 $this->sql('INSERT INTO ' . table_prefix . "diffiehellman( public_key, private_key ) VALUES ( '$dh_key_pub', '$dh_key_priv' );"); |
|
3672 |
|
3673 $return .= "<input type=\"hidden\" name=\"dh_supported\" value=\"true\" /> |
|
3674 <input type=\"hidden\" name=\"dh_public_key\" value=\"$dh_key_pub\" /> |
|
3675 <input type=\"hidden\" name=\"dh_client_public_key\" value=\"\" />"; |
|
3676 } |
|
3677 else |
|
3678 { |
|
3679 $return .= "<input type=\"hidden\" name=\"dh_supported\" value=\"false\" />"; |
|
3680 } |
|
3681 return $return; |
|
3682 } |
|
3683 |
|
3684 /** |
|
3685 * If you used all the same form fields as the normal login interface, this will take care of DiffieHellman for you and return the key. |
|
3686 * @param string Password field name (defaults to "password") |
|
3687 * @return string |
|
3688 */ |
|
3689 |
|
3690 function get_aes_post($fieldname = 'password') |
|
3691 { |
|
3692 global $db, $session, $paths, $template, $plugins; // Common objects |
|
3693 |
|
3694 $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE); |
|
3695 if ( $_POST['use_crypt'] == 'yes' ) |
|
3696 { |
|
3697 $crypt_key = $this->fetch_public_key($_POST['crypt_key']); |
|
3698 if ( !$crypt_key ) |
|
3699 { |
|
3700 throw new Exception($lang->get('user_err_key_not_found')); |
|
3701 } |
|
3702 $crypt_key = hexdecode($crypt_key); |
|
3703 $data = $aes->decrypt($_POST['crypt_data'], $crypt_key, ENC_HEX); |
|
3704 } |
|
3705 else if ( $_POST['use_crypt'] == 'yes_dh' ) |
|
3706 { |
|
3707 require_once(ENANO_ROOT . '/includes/math.php'); |
|
3708 require_once(ENANO_ROOT . '/includes/diffiehellman.php'); |
|
3709 |
|
3710 global $dh_supported, $_math; |
|
3711 |
|
3712 if ( !$dh_supported ) |
|
3713 { |
|
3714 throw new Exception('Server does not support DiffieHellman, denying request'); |
|
3715 } |
|
3716 |
|
3717 // Fetch private key |
|
3718 $dh_public = $_POST['dh_public_key']; |
|
3719 if ( !preg_match('/^[0-9]+$/', $dh_public) ) |
|
3720 { |
|
3721 throw new Exception('ERR_DH_KEY_NOT_INTEGER'); |
|
3722 } |
|
3723 $q = $db->sql_query('SELECT private_key, key_id FROM ' . table_prefix . "diffiehellman WHERE public_key = '$dh_public';"); |
|
3724 if ( !$q ) |
|
3725 $db->die_json(); |
|
3726 |
|
3727 if ( $db->numrows() < 1 ) |
|
3728 { |
|
3729 throw new Exception('ERR_DH_KEY_NOT_FOUND'); |
|
3730 } |
|
3731 |
|
3732 list($dh_private, $dh_key_id) = $db->fetchrow_num(); |
|
3733 $db->free_result(); |
|
3734 |
|
3735 // We have the private key, now delete the key pair, we no longer need it |
|
3736 $q = $db->sql_query('DELETE FROM ' . table_prefix . "diffiehellman WHERE key_id = $dh_key_id;"); |
|
3737 if ( !$q ) |
|
3738 $db->die_json(); |
|
3739 |
|
3740 // Generate the shared secret |
|
3741 $dh_secret = dh_gen_shared_secret($dh_private, $_POST['dh_client_public_key']); |
|
3742 $dh_secret = $_math->str($dh_secret); |
|
3743 |
|
3744 // Did we get all our math right? |
|
3745 $dh_secret_check = sha1($dh_secret); |
|
3746 $dh_hash = $_POST['crypt_key']; |
|
3747 if ( $dh_secret_check !== $dh_hash ) |
|
3748 { |
|
3749 throw new Exception('ERR_DH_HASH_NO_MATCH'); |
|
3750 } |
|
3751 |
|
3752 // All good! Generate the AES key |
|
3753 $aes_key = substr(sha256($dh_secret), 0, ( AES_BITS / 4 )); |
|
3754 |
|
3755 // decrypt user info |
|
3756 $aes_key = hexdecode($aes_key); |
|
3757 $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE); |
|
3758 $data = $aes->decrypt($_POST['crypt_data'], $aes_key, ENC_HEX); |
|
3759 } |
|
3760 else |
|
3761 { |
|
3762 $data = $_POST[$fieldname]; |
|
3763 } |
|
3764 return $data; |
3514 } |
3765 } |
3515 |
3766 |
3516 /** |
3767 /** |
3517 * Backend code for the JSON login interface. Basically a frontend to the session API that takes all parameters in one huge array. |
3768 * Backend code for the JSON login interface. Basically a frontend to the session API that takes all parameters in one huge array. |
3518 * @param array LoginAPI request |
3769 * @param array LoginAPI request |
3734 $username =& $userinfo['username']; |
3985 $username =& $userinfo['username']; |
3735 $password =& $userinfo['password']; |
3986 $password =& $userinfo['password']; |
3736 |
3987 |
3737 // If we're logging in with a temp password, attach to the login_password_reset hook to send our JSON response |
3988 // If we're logging in with a temp password, attach to the login_password_reset hook to send our JSON response |
3738 // A bit hackish since it just dies with the response :-( |
3989 // A bit hackish since it just dies with the response :-( |
3739 $plugins->attachHook('login_password_reset', '$this->process_login_request(array(\'mode\' => \'respond_password_reset\', \'user_id\' => $row[\'user_id\'], \'temp_password\' => $row[\'temp_password\']));'); |
3990 $plugins->attachHook('login_password_reset', '$this->process_login_request(array(\'mode\' => \'respond_password_reset\', \'user_id\' => $row[\'user_id\'], \'temp_password\' => $this->pk_encrypt($password)));'); |
3740 |
3991 |
3741 // attempt the login |
3992 // attempt the login |
3742 // function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER, $captcha_hash = false, $captcha_code = false) |
3993 // function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER, $captcha_hash = false, $captcha_code = false) |
3743 $login_result = $this->login_without_crypto($username, $password, false, intval($req['level']), @$req['captcha_hash'], @$req['captcha_code'], @$req['remember']); |
3994 $login_result = $this->login_without_crypto($username, $password, false, intval($req['level']), @$req['captcha_hash'], @$req['captcha_code'], @$req['remember']); |
3744 |
3995 |