| 1 | <?php |
|---|
| 2 | |
|---|
| 3 | /** |
|---|
| 4 | * SQL Password Driver |
|---|
| 5 | * |
|---|
| 6 | * Driver for passwords stored in SQL database |
|---|
| 7 | * |
|---|
| 8 | * @version 1.4 |
|---|
| 9 | * @author Aleksander 'A.L.E.C' Machniak <alec@alec.pl> |
|---|
| 10 | * |
|---|
| 11 | */ |
|---|
| 12 | |
|---|
| 13 | function password_save($curpass, $passwd) |
|---|
| 14 | { |
|---|
| 15 | $rcmail = rcmail::get_instance(); |
|---|
| 16 | |
|---|
| 17 | if (!($sql = $rcmail->config->get('password_query'))) |
|---|
| 18 | $sql = 'SELECT update_passwd(%c, %u)'; |
|---|
| 19 | |
|---|
| 20 | if ($dsn = $rcmail->config->get('password_db_dsn')) { |
|---|
| 21 | // #1486067: enable new_link option |
|---|
| 22 | if (is_array($dsn) && empty($dsn['new_link'])) |
|---|
| 23 | $dsn['new_link'] = true; |
|---|
| 24 | else if (!is_array($dsn) && !preg_match('/\?new_link=true/', $dsn)) |
|---|
| 25 | $dsn .= '?new_link=true'; |
|---|
| 26 | |
|---|
| 27 | $db = new rcube_mdb2($dsn, '', FALSE); |
|---|
| 28 | $db->set_debug((bool)$rcmail->config->get('sql_debug')); |
|---|
| 29 | $db->db_connect('w'); |
|---|
| 30 | } else { |
|---|
| 31 | $db = $rcmail->get_dbh(); |
|---|
| 32 | } |
|---|
| 33 | |
|---|
| 34 | if ($err = $db->is_error()) |
|---|
| 35 | return PASSWORD_ERROR; |
|---|
| 36 | |
|---|
| 37 | // crypted password |
|---|
| 38 | if (strpos($sql, '%c') !== FALSE) { |
|---|
| 39 | $salt = ''; |
|---|
| 40 | if (CRYPT_MD5) { |
|---|
| 41 | $len = rand(3, CRYPT_SALT_LENGTH); |
|---|
| 42 | } else if (CRYPT_STD_DES) { |
|---|
| 43 | $len = 2; |
|---|
| 44 | } else { |
|---|
| 45 | return PASSWORD_CRYPT_ERROR; |
|---|
| 46 | } |
|---|
| 47 | for ($i = 0; $i < $len ; $i++) { |
|---|
| 48 | $salt .= chr(rand(ord('.'), ord('z'))); |
|---|
| 49 | } |
|---|
| 50 | $sql = str_replace('%c', $db->quote(crypt($passwd, CRYPT_MD5 ? '$1$'.$salt.'$' : $salt)), $sql); |
|---|
| 51 | } |
|---|
| 52 | |
|---|
| 53 | // dovecotpw |
|---|
| 54 | if (strpos($sql, '%D') !== FALSE) { |
|---|
| 55 | if (!($dovecotpw = $rcmail->config->get('password_dovecotpw'))) |
|---|
| 56 | $dovecotpw = 'dovecotpw'; |
|---|
| 57 | if (!($method = $rcmail->config->get('password_dovecotpw_method'))) |
|---|
| 58 | $method = 'CRAM-MD5'; |
|---|
| 59 | |
|---|
| 60 | // use common temp dir |
|---|
| 61 | $tmp_dir = $rcmail->config->get('temp_dir'); |
|---|
| 62 | $tmpfile = tempnam($tmp_dir, 'roundcube-'); |
|---|
| 63 | |
|---|
| 64 | $pipe = popen("$dovecotpw -s '$method' > '$tmpfile'", "w"); |
|---|
| 65 | if (!$pipe) { |
|---|
| 66 | unlink($tmpfile); |
|---|
| 67 | return PASSWORD_CRYPT_ERROR; |
|---|
| 68 | } |
|---|
| 69 | else { |
|---|
| 70 | fwrite($pipe, $passwd . "\n", 1+strlen($passwd)); usleep(1000); |
|---|
| 71 | fwrite($pipe, $passwd . "\n", 1+strlen($passwd)); |
|---|
| 72 | pclose($pipe); |
|---|
| 73 | $newpass = trim(file_get_contents($tmpfile), "\n"); |
|---|
| 74 | if (!preg_match('/^\{' . $method . '\}/', $newpass)) { |
|---|
| 75 | return PASSWORD_CRYPT_ERROR; |
|---|
| 76 | } |
|---|
| 77 | if (!$rcmail->config->get('password_dovecotpw_with_method')) |
|---|
| 78 | $newpass = trim(str_replace('{' . $method . '}', '', $newpass)); |
|---|
| 79 | unlink($tmpfile); |
|---|
| 80 | } |
|---|
| 81 | $sql = str_replace('%D', $db->quote($newpass), $sql); |
|---|
| 82 | } |
|---|
| 83 | |
|---|
| 84 | // hashed passwords |
|---|
| 85 | if (preg_match('/%[n|q]/', $sql)) { |
|---|
| 86 | |
|---|
| 87 | if (!extension_loaded('hash')) { |
|---|
| 88 | raise_error(array( |
|---|
| 89 | 'code' => 600, |
|---|
| 90 | 'type' => 'php', |
|---|
| 91 | 'file' => __FILE__, 'line' => __LINE__, |
|---|
| 92 | 'message' => "Password plugin: 'hash' extension not loaded!" |
|---|
| 93 | ), true, false); |
|---|
| 94 | |
|---|
| 95 | return PASSWORD_ERROR; |
|---|
| 96 | } |
|---|
| 97 | |
|---|
| 98 | if (!($hash_algo = strtolower($rcmail->config->get('password_hash_algorithm')))) |
|---|
| 99 | $hash_algo = 'sha1'; |
|---|
| 100 | |
|---|
| 101 | $hash_passwd = hash($hash_algo, $passwd); |
|---|
| 102 | $hash_curpass = hash($hash_algo, $curpass); |
|---|
| 103 | |
|---|
| 104 | if ($rcmail->config->get('password_hash_base64')) { |
|---|
| 105 | $hash_passwd = base64_encode(pack('H*', $hash_passwd)); |
|---|
| 106 | $hash_curpass = base64_encode(pack('H*', $hash_curpass)); |
|---|
| 107 | } |
|---|
| 108 | |
|---|
| 109 | $sql = str_replace('%n', $db->quote($hash_passwd, 'text'), $sql); |
|---|
| 110 | $sql = str_replace('%q', $db->quote($hash_curpass, 'text'), $sql); |
|---|
| 111 | } |
|---|
| 112 | |
|---|
| 113 | // Handle clear text passwords securely (#1487034) |
|---|
| 114 | $sql_vars = array(); |
|---|
| 115 | if (preg_match_all('/%[p|o]/', $sql, $m)) { |
|---|
| 116 | foreach ($m[0] as $var) { |
|---|
| 117 | if ($var == '%p') { |
|---|
| 118 | $sql = preg_replace('/%p/', '?', $sql, 1); |
|---|
| 119 | $sql_vars[] = (string) $passwd; |
|---|
| 120 | } |
|---|
| 121 | else { // %o |
|---|
| 122 | $sql = preg_replace('/%o/', '?', $sql, 1); |
|---|
| 123 | $sql_vars[] = (string) $curpass; |
|---|
| 124 | } |
|---|
| 125 | } |
|---|
| 126 | } |
|---|
| 127 | |
|---|
| 128 | // at least we should always have the local part |
|---|
| 129 | $sql = str_replace('%l', $db->quote($rcmail->user->get_username('local'), 'text'), $sql); |
|---|
| 130 | $sql = str_replace('%d', $db->quote($rcmail->user->get_username('domain'), 'text'), $sql); |
|---|
| 131 | $sql = str_replace('%u', $db->quote($_SESSION['username'],'text'), $sql); |
|---|
| 132 | $sql = str_replace('%h', $db->quote($_SESSION['imap_host'],'text'), $sql); |
|---|
| 133 | |
|---|
| 134 | $res = $db->query($sql, $sql_vars); |
|---|
| 135 | |
|---|
| 136 | if (!$db->is_error()) { |
|---|
| 137 | if (strtolower(substr(trim($query),0,6))=='select') { |
|---|
| 138 | if ($result = $db->fetch_array($res)) |
|---|
| 139 | return PASSWORD_SUCCESS; |
|---|
| 140 | } else { |
|---|
| 141 | // This is the good case: 1 row updated |
|---|
| 142 | if ($db->affected_rows($res) == 1) |
|---|
| 143 | return PASSWORD_SUCCESS; |
|---|
| 144 | // @TODO: Some queries don't affect any rows |
|---|
| 145 | // Should we assume a success if there was no error? |
|---|
| 146 | } |
|---|
| 147 | } |
|---|
| 148 | |
|---|
| 149 | return PASSWORD_ERROR; |
|---|
| 150 | } |
|---|
| 151 | |
|---|
| 152 | ?> |
|---|