diff options
Diffstat (limited to 'plugins/password/drivers')
22 files changed, 2852 insertions, 0 deletions
diff --git a/plugins/password/drivers/chpasswd.php b/plugins/password/drivers/chpasswd.php new file mode 100644 index 000000000..45c56dba3 --- /dev/null +++ b/plugins/password/drivers/chpasswd.php @@ -0,0 +1,54 @@ +<?php + +/** + * chpasswd driver + * + * Driver that adds functionality to change the systems user password via + * the 'chpasswd' command. + * + * For installation instructions please read the README file. + * + * @version 2.0 + * @author Alex Cartwright <acartwright@mutinydesign.co.uk> + * + * Copyright (C) 2005-2013, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_chpasswd_password +{ + public function save($currpass, $newpass) + { + $cmd = rcmail::get_instance()->config->get('password_chpasswd_cmd'); + $username = $_SESSION['username']; + + $handle = popen($cmd, "w"); + fwrite($handle, "$username:$newpass\n"); + + if (pclose($handle) == 0) { + return PASSWORD_SUCCESS; + } + else { + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: Unable to execute $cmd" + ), true, false); + } + + return PASSWORD_ERROR; + } +} diff --git a/plugins/password/drivers/cpanel.php b/plugins/password/drivers/cpanel.php new file mode 100644 index 000000000..663c125ce --- /dev/null +++ b/plugins/password/drivers/cpanel.php @@ -0,0 +1,87 @@ +<?php + +/** + * cPanel Password Driver + * + * Driver that adds functionality to change the users cPanel password. + * Originally written by Fulvio Venturelli <fulvio@venturelli.org> + * + * Completely rewritten using the cPanel API2 call Email::passwdpop + * as opposed to the original coding against the UI, which is a fragile method that + * makes the driver to always return a failure message for any language other than English + * see http://trac.roundcube.net/ticket/1487015 + * + * This driver has been tested with o2switch hosting and seems to work fine. + * + * @version 3.0 + * @author Christian Chech <christian@chech.fr> + * + * Copyright (C) 2005-2013, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_cpanel_password +{ + public function save($curpas, $newpass) + { + require_once 'xmlapi.php'; + + $rcmail = rcmail::get_instance(); + + $this->cuser = $rcmail->config->get('password_cpanel_username'); + + // Setup the xmlapi connection + $this->xmlapi = new xmlapi($rcmail->config->get('password_cpanel_host')); + $this->xmlapi->set_port($rcmail->config->get('password_cpanel_port')); + $this->xmlapi->password_auth($this->cuser, $rcmail->config->get('password_cpanel_password')); + $this->xmlapi->set_output('json'); + $this->xmlapi->set_debug(0); + + if ($this->setPassword($_SESSION['username'], $newpass)) { + return PASSWORD_SUCCESS; + } + else { + return PASSWORD_ERROR; + } + } + + /** + * Change email account password + * + * Returns true on success or false on failure. + * @param string $password email account password + * @return bool + */ + function setPassword($address, $password) + { + if (strpos($address, '@')) { + list($data['email'], $data['domain']) = explode('@', $address); + } + else { + list($data['email'], $data['domain']) = array($address, ''); + } + + $data['password'] = $password; + + $query = $this->xmlapi->api2_query($this->cuser, 'Email', 'passwdpop', $data); + $query = json_decode($query, true); + + if ($query['cpanelresult']['data'][0]['result'] == 1) { + return true; + } + + return false; + } +} diff --git a/plugins/password/drivers/dbmail.php b/plugins/password/drivers/dbmail.php new file mode 100644 index 000000000..120728395 --- /dev/null +++ b/plugins/password/drivers/dbmail.php @@ -0,0 +1,70 @@ +<?php + +/** + * DBMail Password Driver + * + * Driver that adds functionality to change the users DBMail password. + * The code is derrived from the Squirrelmail "Change SASL Password" Plugin + * by Galen Johnson. + * + * It only works with dbmail-users on the same host where Roundcube runs + * and requires shell access and gcc in order to compile the binary. + * + * For installation instructions please read the README file. + * + * @version 1.0 + * + * Copyright (C) 2005-2013, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_dbmail_password +{ + function save($currpass, $newpass) + { + $curdir = RCUBE_PLUGINS_DIR . 'password/helpers'; + $username = escapeshellarg($_SESSION['username']); + $password = escapeshellarg($newpass); + $args = rcmail::get_instance()->config->get('password_dbmail_args', ''); + $command = "$curdir/chgdbmailusers -c $username -w $password $args"; + + if (strlen($command) > 1024) { + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: The command is too long." + ), true, false); + + return PASSWORD_ERROR; + } + + exec($command, $output, $returnvalue); + + if ($returnvalue == 0) { + return PASSWORD_SUCCESS; + } + else { + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: Unable to execute $curdir/chgdbmailusers" + ), true, false); + } + + return PASSWORD_ERROR; + } +} diff --git a/plugins/password/drivers/directadmin.php b/plugins/password/drivers/directadmin.php new file mode 100644 index 000000000..08ade5130 --- /dev/null +++ b/plugins/password/drivers/directadmin.php @@ -0,0 +1,502 @@ +<?php + +/** + * DirectAdmin Password Driver + * + * Driver to change passwords via DirectAdmin Control Panel + * + * @version 2.1 + * @author Victor Benincasa <vbenincasa@gmail.com> + * + * Copyright (C) 2005-2013, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_directadmin_password +{ + public function save($curpass, $passwd) + { + $rcmail = rcmail::get_instance(); + $Socket = new HTTPSocket; + + $da_user = $_SESSION['username']; + $da_curpass = $curpass; + $da_newpass = $passwd; + $da_host = $rcmail->config->get('password_directadmin_host'); + $da_port = $rcmail->config->get('password_directadmin_port'); + + if (strpos($da_user, '@') === false) { + return array('code' => PASSWORD_ERROR, 'message' => 'Change the SYSTEM user password through control panel!'); + } + + $da_host = str_replace('%h', $_SESSION['imap_host'], $da_host); + $da_host = str_replace('%d', $rcmail->user->get_username('domain'), $da_host); + + $Socket->connect($da_host,$da_port); + $Socket->set_method('POST'); + $Socket->query('/CMD_CHANGE_EMAIL_PASSWORD', + array( + 'email' => $da_user, + 'oldpassword' => $da_curpass, + 'password1' => $da_newpass, + 'password2' => $da_newpass, + 'api' => '1' + )); + $response = $Socket->fetch_parsed_body(); + + //DEBUG + //rcube::console("Password Plugin: [USER: $da_user] [HOST: $da_host] - Response: [SOCKET: ".$Socket->result_status_code."] [DA ERROR: ".strip_tags($response['error'])."] [TEXT: ".$response[text]."]"); + + if($Socket->result_status_code != 200) + return array('code' => PASSWORD_CONNECT_ERROR, 'message' => $Socket->error[0]); + elseif($response['error'] == 1) + return array('code' => PASSWORD_ERROR, 'message' => strip_tags($response['text'])); + else + return PASSWORD_SUCCESS; + } +} + + +/** + * Socket communication class. + * + * Originally designed for use with DirectAdmin's API, this class will fill any HTTP socket need. + * + * Very, very basic usage: + * $Socket = new HTTPSocket; + * echo $Socket->get('http://user:pass@somehost.com:2222/CMD_API_SOMEAPI?query=string&this=that'); + * + * @author Phi1 'l0rdphi1' Stier <l0rdphi1@liquenox.net> + * @updates 2.7 and 2.8 by Victor Benincasa <vbenincasa @ gmail.com> + * @package HTTPSocket + * @version 2.8 + */ +class HTTPSocket { + + var $version = '2.8'; + + /* all vars are private except $error, $query_cache, and $doFollowLocationHeader */ + + var $method = 'GET'; + + var $remote_host; + var $remote_port; + var $remote_uname; + var $remote_passwd; + + var $result; + var $result_header; + var $result_body; + var $result_status_code; + + var $lastTransferSpeed; + + var $bind_host; + + var $error = array(); + var $warn = array(); + var $query_cache = array(); + + var $doFollowLocationHeader = TRUE; + var $redirectURL; + + var $extra_headers = array(); + + /** + * Create server "connection". + * + */ + function connect($host, $port = '' ) + { + if (!is_numeric($port)) + { + $port = 2222; + } + + $this->remote_host = $host; + $this->remote_port = $port; + } + + function bind( $ip = '' ) + { + if ( $ip == '' ) + { + $ip = $_SERVER['SERVER_ADDR']; + } + + $this->bind_host = $ip; + } + + /** + * Change the method being used to communicate. + * + * @param string|null request method. supports GET, POST, and HEAD. default is GET + */ + function set_method( $method = 'GET' ) + { + $this->method = strtoupper($method); + } + + /** + * Specify a username and password. + * + * @param string|null username. defualt is null + * @param string|null password. defualt is null + */ + function set_login( $uname = '', $passwd = '' ) + { + if ( strlen($uname) > 0 ) + { + $this->remote_uname = $uname; + } + + if ( strlen($passwd) > 0 ) + { + $this->remote_passwd = $passwd; + } + + } + + /** + * Query the server + * + * @param string containing properly formatted server API. See DA API docs and examples. Http:// URLs O.K. too. + * @param string|array query to pass to url + * @param int if connection KB/s drops below value here, will drop connection + */ + function query( $request, $content = '', $doSpeedCheck = 0 ) + { + $this->error = $this->warn = array(); + $this->result_status_code = NULL; + + // is our request a http(s):// ... ? + if (preg_match('/^(http|https):\/\//i',$request)) + { + $location = parse_url($request); + $this->connect($location['host'],$location['port']); + $this->set_login($location['user'],$location['pass']); + + $request = $location['path']; + $content = $location['query']; + + if ( strlen($request) < 1 ) + { + $request = '/'; + } + + } + + $array_headers = array( + 'User-Agent' => "HTTPSocket/$this->version", + 'Host' => ( $this->remote_port == 80 ? parse_url($this->remote_host,PHP_URL_HOST) : parse_url($this->remote_host,PHP_URL_HOST).":".$this->remote_port ), + 'Accept' => '*/*', + 'Connection' => 'Close' ); + + foreach ( $this->extra_headers as $key => $value ) + { + $array_headers[$key] = $value; + } + + $this->result = $this->result_header = $this->result_body = ''; + + // was content sent as an array? if so, turn it into a string + if (is_array($content)) + { + $pairs = array(); + + foreach ( $content as $key => $value ) + { + $pairs[] = "$key=".urlencode($value); + } + + $content = join('&',$pairs); + unset($pairs); + } + + $OK = TRUE; + + // instance connection + if ($this->bind_host) + { + $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + socket_bind($socket,$this->bind_host); + + if (!@socket_connect($socket,$this->remote_host,$this->remote_port)) + { + $OK = FALSE; + } + + } + else + { + $socket = @fsockopen( $this->remote_host, $this->remote_port, $sock_errno, $sock_errstr, 10 ); + } + + if ( !$socket || !$OK ) + { + $this->error[] = "Can't create socket connection to $this->remote_host:$this->remote_port."; + return 0; + } + + // if we have a username and password, add the header + if ( isset($this->remote_uname) && isset($this->remote_passwd) ) + { + $array_headers['Authorization'] = 'Basic '.base64_encode("$this->remote_uname:$this->remote_passwd"); + } + + // for DA skins: if $this->remote_passwd is NULL, try to use the login key system + if ( isset($this->remote_uname) && $this->remote_passwd == NULL ) + { + $array_headers['Cookie'] = "session={$_SERVER['SESSION_ID']}; key={$_SERVER['SESSION_KEY']}"; + } + + // if method is POST, add content length & type headers + if ( $this->method == 'POST' ) + { + $array_headers['Content-type'] = 'application/x-www-form-urlencoded'; + $array_headers['Content-length'] = strlen($content); + } + // else method is GET or HEAD. we don't support anything else right now. + else + { + if ($content) + { + $request .= "?$content"; + } + } + + // prepare query + $query = "$this->method $request HTTP/1.0\r\n"; + foreach ( $array_headers as $key => $value ) + { + $query .= "$key: $value\r\n"; + } + $query .= "\r\n"; + + // if POST we need to append our content + if ( $this->method == 'POST' && $content ) + { + $query .= "$content\r\n\r\n"; + } + + // query connection + if ($this->bind_host) + { + socket_write($socket,$query); + + // now load results + while ( $out = socket_read($socket,2048) ) + { + $this->result .= $out; + } + } + else + { + fwrite( $socket, $query, strlen($query) ); + + // now load results + $this->lastTransferSpeed = 0; + $status = socket_get_status($socket); + $startTime = time(); + $length = 0; + while ( !feof($socket) && !$status['timed_out'] ) + { + $chunk = fgets($socket,1024); + $length += strlen($chunk); + $this->result .= $chunk; + + $elapsedTime = time() - $startTime; + + if ( $elapsedTime > 0 ) + { + $this->lastTransferSpeed = ($length/1024)/$elapsedTime; + } + + if ( $doSpeedCheck > 0 && $elapsedTime > 5 && $this->lastTransferSpeed < $doSpeedCheck ) + { + $this->warn[] = "kB/s for last 5 seconds is below 50 kB/s (~".( ($length/1024)/$elapsedTime )."), dropping connection..."; + $this->result_status_code = 503; + break; + } + + } + + if ( $this->lastTransferSpeed == 0 ) + { + $this->lastTransferSpeed = $length/1024; + } + + } + + list($this->result_header,$this->result_body) = preg_split("/\r\n\r\n/",$this->result,2); + + if ($this->bind_host) + { + socket_close($socket); + } + else + { + fclose($socket); + } + + $this->query_cache[] = $query; + + + $headers = $this->fetch_header(); + + // what return status did we get? + if (!$this->result_status_code) + { + preg_match("#HTTP/1\.. (\d+)#",$headers[0],$matches); + $this->result_status_code = $matches[1]; + } + + // did we get the full file? + if ( !empty($headers['content-length']) && $headers['content-length'] != strlen($this->result_body) ) + { + $this->result_status_code = 206; + } + + // now, if we're being passed a location header, should we follow it? + if ($this->doFollowLocationHeader) + { + if ($headers['location']) + { + $this->redirectURL = $headers['location']; + $this->query($headers['location']); + } + } + } + + function getTransferSpeed() + { + return $this->lastTransferSpeed; + } + + /** + * The quick way to get a URL's content :) + * + * @param string URL + * @param boolean return as array? (like PHP's file() command) + * @return string result body + */ + function get($location, $asArray = FALSE ) + { + $this->query($location); + + if ( $this->get_status_code() == 200 ) + { + if ($asArray) + { + return preg_split("/\n/",$this->fetch_body()); + } + + return $this->fetch_body(); + } + + return FALSE; + } + + /** + * Returns the last status code. + * 200 = OK; + * 403 = FORBIDDEN; + * etc. + * + * @return int status code + */ + function get_status_code() + { + return $this->result_status_code; + } + + /** + * Adds a header, sent with the next query. + * + * @param string header name + * @param string header value + */ + function add_header($key,$value) + { + $this->extra_headers[$key] = $value; + } + + /** + * Clears any extra headers. + * + */ + function clear_headers() + { + $this->extra_headers = array(); + } + + /** + * Return the result of a query. + * + * @return string result + */ + function fetch_result() + { + return $this->result; + } + + /** + * Return the header of result (stuff before body). + * + * @param string (optional) header to return + * @return array result header + */ + function fetch_header( $header = '' ) + { + $array_headers = preg_split("/\r\n/",$this->result_header); + $array_return = array( 0 => $array_headers[0] ); + unset($array_headers[0]); + + foreach ( $array_headers as $pair ) + { + list($key,$value) = preg_split("/: /",$pair,2); + $array_return[strtolower($key)] = $value; + } + + if ( $header != '' ) + { + return $array_return[strtolower($header)]; + } + + return $array_return; + } + + /** + * Return the body of result (stuff after header). + * + * @return string result body + */ + function fetch_body() + { + return $this->result_body; + } + + /** + * Return parsed body in array format. + * + * @return array result parsed + */ + function fetch_parsed_body() + { + parse_str($this->result_body,$x); + return $x; + } + +} diff --git a/plugins/password/drivers/domainfactory.php b/plugins/password/drivers/domainfactory.php new file mode 100644 index 000000000..95088e9dd --- /dev/null +++ b/plugins/password/drivers/domainfactory.php @@ -0,0 +1,100 @@ +<?php + +/** + * domainFACTORY Password Driver + * + * Driver to change passwords with the hosting provider domainFACTORY. + * http://www.df.eu/ + * + * @version 2.1 + * @author Till Krüss <me@tillkruess.com> + * @link http://tillkruess.com/projects/roundcube/ + * + * Copyright (C) 2005-2014, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_domainfactory_password +{ + function save($curpass, $passwd) + { + $rcmail = rcmail::get_instance(); + + if (is_null($curpass)) { + $curpass = $rcmail->decrypt($_SESSION['password']); + } + + if ($ch = curl_init()) { + // initial login + curl_setopt_array($ch, array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_URL => 'https://ssl.df.eu/chmail.php', + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => http_build_query(array( + 'login' => $rcmail->user->get_username(), + 'pwd' => $curpass, + 'action' => 'change' + )) + )); + + if ($result = curl_exec($ch)) { + // login successful, get token! + $postfields = array( + 'pwd1' => $passwd, + 'pwd2' => $passwd, + 'action[update]' => 'Speichern' + ); + + preg_match_all('~<input name="(.+?)" type="hidden" value="(.+?)">~i', $result, $fields); + foreach ($fields[1] as $field_key => $field_name) { + $postfields[$field_name] = $fields[2][$field_key]; + } + + // change password + $ch = curl_copy_handle($ch); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postfields)); + if ($result = curl_exec($ch)) { + // has the password been changed? + if (strpos($result, 'Einstellungen erfolgreich') !== false) { + return PASSWORD_SUCCESS; + } + + // show error message(s) if possible + if (strpos($result, '<div class="d-msg-text">') !== false) { + preg_match_all('#<div class="d-msg-text">(.*?)</div>#s', $result, $errors); + if (isset($errors[1])) { + $error_message = ''; + foreach ($errors[1] as $error) { + $error_message .= trim(mb_convert_encoding( $error, 'UTF-8', 'ISO-8859-15' )).' '; + } + return array('code' => PASSWORD_ERROR, 'message' => $error_message); + } + } + } + else { + return PASSWORD_CONNECT_ERROR; + } + } + else { + return PASSWORD_CONNECT_ERROR; + } + } + else { + return PASSWORD_CONNECT_ERROR; + } + + return PASSWORD_ERROR; + } +} diff --git a/plugins/password/drivers/expect.php b/plugins/password/drivers/expect.php new file mode 100644 index 000000000..57fe905ee --- /dev/null +++ b/plugins/password/drivers/expect.php @@ -0,0 +1,73 @@ +<?php + +/** + * expect driver + * + * Driver that adds functionality to change the systems user password via + * the 'expect' command. + * + * For installation instructions please read the README file. + * + * @version 2.0 + * @author Andy Theuninck <gohanman@gmail.com) + * + * Based on chpasswd roundcubemail password driver by + * @author Alex Cartwright <acartwright@mutinydesign.co.uk) + * and expect horde passwd driver by + * @author Gaudenz Steinlin <gaudenz@soziologie.ch> + * + * Configuration settings: + * password_expect_bin => location of expect (e.g. /usr/bin/expect) + * password_expect_script => path to "password-expect" file + * password_expect_params => arguments for the expect script + * see the password-expect file for details. This is probably + * a good starting default: + * -telent -host localhost -output /tmp/passwd.log -log /tmp/passwd.log + * + * Copyright (C) 2005-2014, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_expect_password +{ + public function save($currpass, $newpass) + { + $rcmail = rcmail::get_instance(); + $bin = $rcmail->config->get('password_expect_bin'); + $script = $rcmail->config->get('password_expect_script'); + $params = $rcmail->config->get('password_expect_params'); + $username = $_SESSION['username']; + + $cmd = $bin . ' -f ' . $script . ' -- ' . $params; + $handle = popen($cmd, "w"); + fwrite($handle, "$username\n"); + fwrite($handle, "$currpass\n"); + fwrite($handle, "$newpass\n"); + + if (pclose($handle) == 0) { + return PASSWORD_SUCCESS; + } + else { + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: Unable to execute $cmd" + ), true, false); + } + + return PASSWORD_ERROR; + } +} diff --git a/plugins/password/drivers/gearman.php b/plugins/password/drivers/gearman.php new file mode 100644 index 000000000..983aee046 --- /dev/null +++ b/plugins/password/drivers/gearman.php @@ -0,0 +1,70 @@ +<?php + +/** + * Gearman Password Driver + * + * Payload is json string containing username, oldPassword and newPassword + * Return value is a json string saying result: true if success. + * + * @version 1.0 + * @author Mohammad Anwari <mdamt@mdamt.net> + * + * Copyright (C) 2005-2014, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_gearman_password +{ + function save($currpass, $newpass) + { + if (extension_loaded('gearman')) { + $rcmail = rcmail::get_instance(); + $user = $_SESSION['username']; + $payload = array( + 'username' => $user, + 'oldPassword' => $currpass, + 'newPassword' => $newpass, + ); + + $gmc = new GearmanClient(); + $gmc->addServer($rcmail->config->get('password_gearman_host')); + + $result = $gmc->doNormal('setPassword', json_encode($payload)); + $success = json_decode($result); + + if ($success && $success->result == 1) { + return PASSWORD_SUCCESS; + } + else { + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: Gearman authentication failed for user $user: $error" + ), true, false); + } + } + else { + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: PECL Gearman module not loaded" + ), true, false); + } + + return PASSWORD_ERROR; + } +} diff --git a/plugins/password/drivers/hmail.php b/plugins/password/drivers/hmail.php new file mode 100644 index 000000000..49f7f6cf4 --- /dev/null +++ b/plugins/password/drivers/hmail.php @@ -0,0 +1,76 @@ +<?php + +/** + * hMailserver password driver + * + * @version 2.0 + * @author Roland 'rosali' Liebl <myroundcube@mail4us.net> + * + * Copyright (C) 2005-2014, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_hmail_password +{ + public function save($curpass, $passwd) + { + $rcmail = rcmail::get_instance(); + + if ($curpass == '' || $passwd == '') { + return PASSWORD_ERROR; + } + + try { + $remote = $rcmail->config->get('hmailserver_remote_dcom', false); + if ($remote) + $obApp = new COM("hMailServer.Application", $rcmail->config->get('hmailserver_server')); + else + $obApp = new COM("hMailServer.Application"); + } + catch (Exception $e) { + rcube::write_log('errors', "Plugin password (hmail driver): " . trim(strip_tags($e->getMessage()))); + rcube::write_log('errors', "Plugin password (hmail driver): This problem is often caused by DCOM permissions not being set."); + return PASSWORD_ERROR; + } + + $username = $rcmail->user->data['username']; + if (strstr($username,'@')){ + $temparr = explode('@', $username); + $domain = $temparr[1]; + } + else { + $domain = $rcmail->config->get('username_domain',false); + if (!$domain) { + rcube::write_log('errors','Plugin password (hmail driver): $config[\'username_domain\'] is not defined.'); + return PASSWORD_ERROR; + } + $username = $username . "@" . $domain; + } + + $obApp->Authenticate($username, $curpass); + try { + $obDomain = $obApp->Domains->ItemByName($domain); + $obAccount = $obDomain->Accounts->ItemByAddress($username); + $obAccount->Password = $passwd; + $obAccount->Save(); + return PASSWORD_SUCCESS; + } + catch (Exception $e) { + rcube::write_log('errors', "Plugin password (hmail driver): " . trim(strip_tags($e->getMessage()))); + rcube::write_log('errors', "Plugin password (hmail driver): This problem is often caused by DCOM permissions not being set."); + return PASSWORD_ERROR; + } + } +} diff --git a/plugins/password/drivers/kpasswd.php b/plugins/password/drivers/kpasswd.php new file mode 100644 index 000000000..ce245b315 --- /dev/null +++ b/plugins/password/drivers/kpasswd.php @@ -0,0 +1,45 @@ +<?php + +/** + * kpasswd Driver + * + * Driver that adds functionality to change the systems user password via + * the 'kpasswd' command. + * + * For installation instructions please read the README file. + * + * @version 1.0 + * @author Peter Allgeyer <peter.allgeyer@salzburgresearch.at> + * + * Based on chpasswd roundcubemail password driver by + * @author Alex Cartwright <acartwright@mutinydesign.co.uk> + */ + +class rcube_kpasswd_password +{ + public function save($currpass, $newpass) + { + $bin = rcmail::get_instance()->config->get('password_kpasswd_cmd', '/usr/bin/kpasswd'); + $username = $_SESSION['username']; + $cmd = $bin . ' "' . $username . '" 2>&1'; + + $handle = popen($cmd, "w"); + fwrite($handle, $currpass."\n"); + fwrite($handle, $newpass."\n"); + fwrite($handle, $newpass."\n"); + + if (pclose($handle) == 0) { + return PASSWORD_SUCCESS; + } + else { + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: Unable to execute $cmd" + ), true, false); + } + + return PASSWORD_ERROR; + } +} diff --git a/plugins/password/drivers/ldap.php b/plugins/password/drivers/ldap.php new file mode 100644 index 000000000..6ed5ada04 --- /dev/null +++ b/plugins/password/drivers/ldap.php @@ -0,0 +1,381 @@ +<?php + +/** + * LDAP Password Driver + * + * Driver for passwords stored in LDAP + * This driver use the PEAR Net_LDAP2 class (http://pear.php.net/package/Net_LDAP2). + * + * @version 2.0 + * @author Edouard MOREAU <edouard.moreau@ensma.fr> + * + * method hashPassword based on code from the phpLDAPadmin development team (http://phpldapadmin.sourceforge.net/). + * method randomSalt based on code from the phpLDAPadmin development team (http://phpldapadmin.sourceforge.net/). + * + * Copyright (C) 2005-2014, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_ldap_password +{ + public function save($curpass, $passwd) + { + $rcmail = rcmail::get_instance(); + require_once 'Net/LDAP2.php'; + + // Building user DN + if ($userDN = $rcmail->config->get('password_ldap_userDN_mask')) { + $userDN = self::substitute_vars($userDN); + } + else { + $userDN = $this->search_userdn($rcmail); + } + + if (empty($userDN)) { + return PASSWORD_CONNECT_ERROR; + } + + // Connection Method + switch($rcmail->config->get('password_ldap_method')) { + case 'admin': + $binddn = $rcmail->config->get('password_ldap_adminDN'); + $bindpw = $rcmail->config->get('password_ldap_adminPW'); + break; + case 'user': + default: + $binddn = $userDN; + $bindpw = $curpass; + break; + } + + // Configuration array + $ldapConfig = array ( + 'binddn' => $binddn, + 'bindpw' => $bindpw, + 'basedn' => $rcmail->config->get('password_ldap_basedn'), + 'host' => $rcmail->config->get('password_ldap_host'), + 'port' => $rcmail->config->get('password_ldap_port'), + 'starttls' => $rcmail->config->get('password_ldap_starttls'), + 'version' => $rcmail->config->get('password_ldap_version'), + ); + + // Connecting using the configuration array + $ldap = Net_LDAP2::connect($ldapConfig); + + // Checking for connection error + if (is_a($ldap, 'PEAR_Error')) { + return PASSWORD_CONNECT_ERROR; + } + + $force = $rcmail->config->get('password_ldap_force_replace'); + $pwattr = $rcmail->config->get('password_ldap_pwattr'); + $lchattr = $rcmail->config->get('password_ldap_lchattr'); + $smbpwattr = $rcmail->config->get('password_ldap_samba_pwattr'); + $smblchattr = $rcmail->config->get('password_ldap_samba_lchattr'); + $samba = $rcmail->config->get('password_ldap_samba'); + $encodage = $rcmail->config->get('password_ldap_encodage'); + + // Support multiple userPassword values where desired. + // multiple encodings can be specified separated by '+' (e.g. "cram-md5+ssha") + $encodages = explode('+', $encodage); + $crypted_pass = array(); + + foreach ($encodages as $enc) { + $cpw = self::hash_password($passwd, $enc); + if (!empty($cpw)) { + $crypted_pass[] = $cpw; + } + } + + // Support password_ldap_samba option for backward compat. + if ($samba && !$smbpwattr) { + $smbpwattr = 'sambaNTPassword'; + $smblchattr = 'sambaPwdLastSet'; + } + + // Crypt new password + if (empty($crypted_pass)) { + return PASSWORD_CRYPT_ERROR; + } + + // Crypt new samba password + if ($smbpwattr && !($samba_pass = self::hash_password($passwd, 'samba'))) { + return PASSWORD_CRYPT_ERROR; + } + + // Writing new crypted password to LDAP + $userEntry = $ldap->getEntry($userDN); + if (Net_LDAP2::isError($userEntry)) { + return PASSWORD_CONNECT_ERROR; + } + + if (!$userEntry->replace(array($pwattr => $crypted_pass), $force)) { + return PASSWORD_CONNECT_ERROR; + } + + // Updating PasswordLastChange Attribute if desired + if ($lchattr) { + $current_day = (int)(time() / 86400); + if (!$userEntry->replace(array($lchattr => $current_day), $force)) { + return PASSWORD_CONNECT_ERROR; + } + } + + // Update Samba password and last change fields + if ($smbpwattr) { + $userEntry->replace(array($smbpwattr => $samba_pass), $force); + } + // Update Samba password last change field + if ($smblchattr) { + $userEntry->replace(array($smblchattr => time()), $force); + } + + if (Net_LDAP2::isError($userEntry->update())) { + return PASSWORD_CONNECT_ERROR; + } + + // All done, no error + return PASSWORD_SUCCESS; + } + + /** + * Bind with searchDN and searchPW and search for the user's DN. + * Use search_base and search_filter defined in config file. + * Return the found DN. + */ + function search_userdn($rcmail) + { + $binddn = $rcmail->config->get('password_ldap_searchDN'); + $bindpw = $rcmail->config->get('password_ldap_searchPW'); + + $ldapConfig = array ( + 'basedn' => $rcmail->config->get('password_ldap_basedn'), + 'host' => $rcmail->config->get('password_ldap_host'), + 'port' => $rcmail->config->get('password_ldap_port'), + 'starttls' => $rcmail->config->get('password_ldap_starttls'), + 'version' => $rcmail->config->get('password_ldap_version'), + ); + + // allow anonymous searches + if (!empty($binddn)) { + $ldapConfig['binddn'] = $binddn; + $ldapConfig['bindpw'] = $bindpw; + } + + $ldap = Net_LDAP2::connect($ldapConfig); + + if (is_a($ldap, 'PEAR_Error')) { + return ''; + } + + $base = self::substitute_vars($rcmail->config->get('password_ldap_search_base')); + $filter = self::substitute_vars($rcmail->config->get('password_ldap_search_filter')); + $options = array ( + 'scope' => 'sub', + 'attributes' => array(), + ); + + $result = $ldap->search($base, $filter, $options); + $ldap->done(); + if (is_a($result, 'PEAR_Error') || ($result->count() != 1)) { + return ''; + } + + return $result->current()->dn(); + } + + /** + * Substitute %login, %name, %domain, %dc in $str + * See plugin config for details + */ + static function substitute_vars($str) + { + $str = str_replace('%login', $_SESSION['username'], $str); + $str = str_replace('%l', $_SESSION['username'], $str); + + $parts = explode('@', $_SESSION['username']); + + if (count($parts) == 2) { + $dc = 'dc='.strtr($parts[1], array('.' => ',dc=')); // hierarchal domain string + + $str = str_replace('%name', $parts[0], $str); + $str = str_replace('%n', $parts[0], $str); + $str = str_replace('%dc', $dc, $str); + $str = str_replace('%domain', $parts[1], $str); + $str = str_replace('%d', $parts[1], $str); + } + + return $str; + } + + /** + * Code originaly from the phpLDAPadmin development team + * http://phpldapadmin.sourceforge.net/ + * + * Hashes a password and returns the hash based on the specified enc_type + */ + static function hash_password($password_clear, $encodage_type) + { + $encodage_type = strtolower($encodage_type); + switch ($encodage_type) { + case 'crypt': + $crypted_password = '{CRYPT}' . crypt($password_clear, self::random_salt(2)); + break; + + case 'ext_des': + /* Extended DES crypt. see OpenBSD crypt man page */ + if (!defined('CRYPT_EXT_DES') || CRYPT_EXT_DES == 0) { + /* Your system crypt library does not support extended DES encryption */ + return false; + } + + $crypted_password = '{CRYPT}' . crypt($password_clear, '_' . self::random_salt(8)); + break; + + case 'md5crypt': + if (!defined('CRYPT_MD5') || CRYPT_MD5 == 0) { + /* Your system crypt library does not support md5crypt encryption */ + return false; + } + + $crypted_password = '{CRYPT}' . crypt($password_clear, '$1$' . self::random_salt(9)); + break; + + case 'blowfish': + if (!defined('CRYPT_BLOWFISH') || CRYPT_BLOWFISH == 0) { + /* Your system crypt library does not support blowfish encryption */ + return false; + } + + $rcmail = rcmail::get_instance(); + $cost = (int) $rcmail->config->get('password_blowfish_cost'); + $cost = $cost < 4 || $cost > 31 ? 12 : $cost; + $prefix = sprintf('$2a$%02d$', $cost); + + $crypted_password = '{CRYPT}' . crypt($password_clear, $prefix . self::random_salt(22)); + break; + + case 'md5': + $crypted_password = '{MD5}' . base64_encode(pack('H*', md5($password_clear))); + break; + + case 'sha': + if (function_exists('sha1')) { + /* Use PHP 4.3.0+ sha1 function, if it is available */ + $crypted_password = '{SHA}' . base64_encode(pack('H*', sha1($password_clear))); + } + else if (function_exists('hash')) { + $crypted_password = '{SHA}' . base64_encode(hash('sha1', $password_clear, true)); + } + else if (function_exists('mhash')) { + $crypted_password = '{SHA}' . base64_encode(mhash(MHASH_SHA1, $password_clear)); + } + else { + /* Your PHP install does not have the mhash()/hash() nor sha1() function */ + return false; + } + break; + + case 'ssha': + $salt = substr(pack('h*', md5(mt_rand())), 0, 8); + + if (function_exists('mhash') && function_exists('mhash_keygen_s2k')) { + $salt = mhash_keygen_s2k(MHASH_SHA1, $password_clear, $salt, 4); + $password = mhash(MHASH_SHA1, $password_clear . $salt); + } + else if (function_exists('sha1')) { + $salt = substr(pack("H*", sha1($salt . $password_clear)), 0, 4); + $password = sha1($password_clear . $salt, true); + } + else if (function_exists('hash')) { + $salt = substr(pack("H*", hash('sha1', $salt . $password_clear)), 0, 4); + $password = hash('sha1', $password_clear . $salt, true); + } + + if ($password) { + $crypted_password = '{SSHA}' . base64_encode($password . $salt); + } + else { + /* Your PHP install does not have the mhash()/hash() nor sha1() function */ + return false; + } + break; + + + case 'smd5': + $salt = substr(pack('h*', md5(mt_rand())), 0, 8); + + if (function_exists('mhash') && function_exists('mhash_keygen_s2k')) { + $salt = mhash_keygen_s2k(MHASH_MD5, $password_clear, $salt, 4); + $password = mhash(MHASH_MD5, $password_clear . $salt); + } + else if (function_exists('hash')) { + $salt = substr(pack("H*", hash('md5', $salt . $password_clear)), 0, 4); + $password = hash('md5', $password_clear . $salt, true); + } + else { + $salt = substr(pack("H*", md5($salt . $password_clear)), 0, 4); + $password = md5($password_clear . $salt, true); + } + + $crypted_password = '{SMD5}' . base64_encode($password . $salt); + break; + + case 'samba': + if (function_exists('hash')) { + $crypted_password = hash('md4', rcube_charset::convert($password_clear, RCUBE_CHARSET, 'UTF-16LE')); + $crypted_password = strtoupper($crypted_password); + } + else { + /* Your PHP install does not have the hash() function */ + return false; + } + break; + + case 'ad': + $crypted_password = rcube_charset::convert('"' . $password_clear . '"', RCUBE_CHARSET, 'UTF-16LE'); + break; + + case 'cram-md5': + require_once __DIR__ . '/../helpers/dovecot_hmacmd5.php'; + return dovecot_hmacmd5($password_clear); + break; + + case 'clear': + default: + $crypted_password = $password_clear; + } + + return $crypted_password; + } + + /** + * Code originaly from the phpLDAPadmin development team + * http://phpldapadmin.sourceforge.net/ + * + * Used to generate a random salt for crypt-style passwords + */ + static function random_salt($length) + { + $possible = '0123456789' . 'abcdefghijklmnopqrstuvwxyz' . 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' . './'; + $str = ''; + + while (strlen($str) < $length) { + $str .= substr($possible, (rand() % strlen($possible)), 1); + } + + return $str; + } +} diff --git a/plugins/password/drivers/ldap_simple.php b/plugins/password/drivers/ldap_simple.php new file mode 100644 index 000000000..9123ea81f --- /dev/null +++ b/plugins/password/drivers/ldap_simple.php @@ -0,0 +1,238 @@ +<?php + +/** + * Simple LDAP Password Driver + * + * Driver for passwords stored in LDAP + * This driver is based on Edouard's LDAP Password Driver, but does not + * require PEAR's Net_LDAP2 to be installed + * + * @version 2.0 + * @author Wout Decre <wout@canodus.be> + * @author Aleksander Machniak <machniak@kolabsys.com> + * + * Copyright (C) 2005-2014, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_ldap_simple_password +{ + private $debug = false; + + function save($curpass, $passwd) + { + $rcmail = rcmail::get_instance(); + + $this->debug = $rcmail->config->get('ldap_debug'); + + $ldap_host = $rcmail->config->get('password_ldap_host'); + $ldap_port = $rcmail->config->get('password_ldap_port'); + + $this->_debug("C: Connect to $ldap_host:$ldap_port"); + + // Connect + if (!$ds = ldap_connect($ldap_host, $ldap_port)) { + $this->_debug("S: NOT OK"); + + rcube::raise_error(array( + 'code' => 100, 'type' => 'ldap', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Could not connect to LDAP server" + ), + true); + + return PASSWORD_CONNECT_ERROR; + } + + $this->_debug("S: OK"); + + // Set protocol version + ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, $rcmail->config->get('password_ldap_version')); + + // Start TLS + if ($rcmail->config->get('password_ldap_starttls')) { + if (!ldap_start_tls($ds)) { + ldap_unbind($ds); + return PASSWORD_CONNECT_ERROR; + } + } + + // include 'ldap' driver, we share some static methods with it + require_once INSTALL_PATH . 'plugins/password/drivers/ldap.php'; + + // other plugins might want to modify user DN + $plugin = $rcmail->plugins->exec_hook('password_ldap_bind', array( + 'user_dn' => '', 'conn' => $ds)); + + // Build user DN + if (!empty($plugin['user_dn'])) { + $user_dn = $plugin['user_dn']; + } + else if ($user_dn = $rcmail->config->get('password_ldap_userDN_mask')) { + $user_dn = rcube_ldap_password::substitute_vars($user_dn); + } + else { + $user_dn = $this->search_userdn($rcmail, $ds); + } + + if (empty($user_dn)) { + ldap_unbind($ds); + return PASSWORD_CONNECT_ERROR; + } + + // Connection method + switch ($rcmail->config->get('password_ldap_method')) { + case 'admin': + $binddn = $rcmail->config->get('password_ldap_adminDN'); + $bindpw = $rcmail->config->get('password_ldap_adminPW'); + break; + case 'user': + default: + $binddn = $user_dn; + $bindpw = $curpass; + break; + } + + $lchattr = $rcmail->config->get('password_ldap_lchattr'); + $pwattr = $rcmail->config->get('password_ldap_pwattr'); + $smbpwattr = $rcmail->config->get('password_ldap_samba_pwattr'); + $smblchattr = $rcmail->config->get('password_ldap_samba_lchattr'); + $samba = $rcmail->config->get('password_ldap_samba'); + $pass_mode = $rcmail->config->get('password_ldap_encodage'); + $crypted_pass = rcube_ldap_password::hash_password($passwd, $pass_mode); + + // Support password_ldap_samba option for backward compat. + if ($samba && !$smbpwattr) { + $smbpwattr = 'sambaNTPassword'; + $smblchattr = 'sambaPwdLastSet'; + } + + // Crypt new password + if (!$crypted_pass) { + return PASSWORD_CRYPT_ERROR; + } + + // Crypt new Samba password + if ($smbpwattr && !($samba_pass = rcube_ldap_password::hash_password($passwd, 'samba'))) { + return PASSWORD_CRYPT_ERROR; + } + + $this->_debug("C: Bind $binddn, pass: **** [" . strlen($bindpw) . "]"); + + // Bind + if (!ldap_bind($ds, $binddn, $bindpw)) { + $this->_debug("S: ".ldap_error($ds)); + + ldap_unbind($ds); + + return PASSWORD_CONNECT_ERROR; + } + + $this->_debug("S: OK"); + + $entry[$pwattr] = $crypted_pass; + + // Update PasswordLastChange Attribute if desired + if ($lchattr) { + $entry[$lchattr] = (int)(time() / 86400); + } + + // Update Samba password + if ($smbpwattr) { + $entry[$smbpwattr] = $samba_pass; + } + + // Update Samba password last change + if ($smblchattr) { + $entry[$smblchattr] = time(); + } + + $this->_debug("C: Modify $user_dn: " . print_r($entry, true)); + + if (!ldap_modify($ds, $user_dn, $entry)) { + $this->_debug("S: ".ldap_error($ds)); + + ldap_unbind($ds); + + return PASSWORD_CONNECT_ERROR; + } + + $this->_debug("S: OK"); + + // All done, no error + ldap_unbind($ds); + + return PASSWORD_SUCCESS; + } + + /** + * Bind with searchDN and searchPW and search for the user's DN + * Use search_base and search_filter defined in config file + * Return the found DN + */ + function search_userdn($rcmail, $ds) + { + $search_user = $rcmail->config->get('password_ldap_searchDN'); + $search_pass = $rcmail->config->get('password_ldap_searchPW'); + $search_base = $rcmail->config->get('password_ldap_search_base'); + $search_filter = $rcmail->config->get('password_ldap_search_filter'); + + if (empty($search_filter)) { + return false; + } + + $this->_debug("C: Bind " . ($search_user ? $search_user : '[anonymous]')); + + // Bind + if (!ldap_bind($ds, $search_user, $search_pass)) { + $this->_debug("S: ".ldap_error($ds)); + return false; + } + + $this->_debug("S: OK"); + + $search_base = rcube_ldap_password::substitute_vars($search_base); + $search_filter = rcube_ldap_password::substitute_vars($search_filter); + + $this->_debug("C: Search $search_base for $search_filter"); + + // Search for the DN + if (!$sr = ldap_search($ds, $search_base, $search_filter)) { + $this->_debug("S: ".ldap_error($ds)); + return false; + } + + $found = ldap_count_entries($ds, $sr); + + $this->_debug("S: OK [found $found records]"); + + // If no or more entries were found, return false + if ($found != 1) { + return false; + } + + return ldap_get_dn($ds, ldap_first_entry($ds, $sr)); + } + + /** + * Prints debug info to the log + */ + private function _debug($str) + { + if ($this->debug) { + rcube::write_log('ldap', $str); + } + } +} diff --git a/plugins/password/drivers/pam.php b/plugins/password/drivers/pam.php new file mode 100644 index 000000000..cd5a92f49 --- /dev/null +++ b/plugins/password/drivers/pam.php @@ -0,0 +1,58 @@ +<?php + +/** + * PAM Password Driver + * + * @version 2.0 + * @author Aleksander Machniak + * + * Copyright (C) 2005-2013, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_pam_password +{ + function save($currpass, $newpass) + { + $user = $_SESSION['username']; + $error = ''; + + if (extension_loaded('pam') || extension_loaded('pam_auth')) { + if (pam_auth($user, $currpass, $error, false)) { + if (pam_chpass($user, $currpass, $newpass)) { + return PASSWORD_SUCCESS; + } + } + else { + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: PAM authentication failed for user $user: $error" + ), true, false); + } + } + else { + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: PECL-PAM module not loaded" + ), true, false); + } + + return PASSWORD_ERROR; + } +} diff --git a/plugins/password/drivers/plesk.php b/plugins/password/drivers/plesk.php new file mode 100644 index 000000000..db9ad9efd --- /dev/null +++ b/plugins/password/drivers/plesk.php @@ -0,0 +1,241 @@ +<?php + +/** + * Roundcube Password Driver for Plesk-RPC. + * + * This driver changes a E-Mail-Password via Plesk-RPC + * Deps: PHP-Curl, SimpleXML + * + * @author Cyrill von Wattenwyl <cyrill.vonwattenwyl@adfinis-sygroup.ch> + * @copyright Adfinis SyGroup AG, 2014 + * + * Config needed: + * $config['password_plesk_host'] = '10.0.0.5'; + * $config['password_plesk_user'] = 'admin'; + * $config['password_plesk_pass'] = 'pass'; + * $config['password_plesk_rpc_port'] = 8443; + * $config['password_plesk_rpc_path'] = enterprise/control/agent.php; + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +/** + * Roundcube Password Driver Class + * + * See {ROUNDCUBE_ROOT}/plugins/password/README for API description + * + * @author Cyrill von Wattenwyl <cyrill.vonwattenwyl@adfinis-sygroup.ch> + */ +class rcube_plesk_password +{ + /** + * this method is called from roundcube to change the password + * + * roundcube allready validated the old password so we just need to change it at this point + * + * @author Cyrill von Wattenwyl <cyrill.vonwattenwyl@adfinis-sygroup.ch> + * @param string $curpass Current password + * @param string $newpass New password + * @returns int PASSWORD_SUCCESS|PASSWORD_ERROR + */ + function save($currpass, $newpass) + { + // get config + $rcmail = rcmail::get_instance(); + $host = $rcmail->config->get('password_plesk_host'); + $user = $rcmail->config->get('password_plesk_user'); + $pass = $rcmail->config->get('password_plesk_pass'); + $port = $rcmail->config->get('password_plesk_rpc_port'); + $path = $rcmail->config->get('password_plesk_rpc_path'); + + // create plesk-object + $plesk = new plesk_rpc; + $plesk->init($host, $port, $path, $user, $pass); + + // try to change password and return the status + $result = $plesk->change_mailbox_password($_SESSION['username'], $newpass); + //$plesk->destroy(); + + if ($result) { + return PASSWORD_SUCCESS; + } + + return PASSWORD_ERROR; + } +} + + +/** + * Plesk RPC-Class + * + * Striped down version of Plesk-RPC-Class + * Just functions for changing mail-passwords included + * + * Documentation of Plesk RPC-API: http://download1.parallels.com/Plesk/PP11/11.0/Doc/en-US/online/plesk-api-rpc/ + * + * @author Cyrill von Wattenwyl <cyrill.vonwattenwyl@adfinis-sygroup.ch> + */ +class plesk_rpc +{ + /** + * init plesk-rpc via curl + * + * @param string $host plesk host + * @param string $port plesk rpc port + * @param string $path plesk rpc path + * @param string $user plesk user + * @param string $user plesk password + * @returns void + */ + function init($host, $port, $path, $user, $pass) + { + $headers = array( + sprintf("HTTP_AUTH_LOGIN: %s", $user), + sprintf("HTTP_AUTH_PASSWD: %s", $pass), + "Content-Type: text/xml" + ); + + $url = sprintf("https://%s:%s/%s", $host, $port, $path); + $this->curl = curl_init(); + + curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT , 5); + curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST , 0); + curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER , false); + curl_setopt($this->curl, CURLOPT_HTTPHEADER , $headers); + curl_setopt($this->curl, CURLOPT_URL , $url); + } + + /** + * send a request to the plesk + * + * @param string $packet XML-Packet to send to Plesk + * @returns bool request was successfull or not + */ + function send_request($packet) + { + curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($this->curl, CURLOPT_POSTFIELDS, $packet); + $retval = curl_exec($this->curl); + + return $retval; + } + + /** + * close curl + */ + function destroy(){ + curl_close($this->curl); + } + + /** + * Creates an Initial SimpleXML-Object for Plesk-RPC + * + * @returns object SimpleXML object + */ + function get_request_obj() + { + $request = new SimpleXMLElement("<packet></packet>"); + $request->addAttribute("version", "1.6.3.0"); + + return $request; + } + + /** + * Get all hosting-informations of a domain + * + * @param string $domain domain-name + * @returns object SimpleXML object + */ + function domain_info($domain) + { + // build xml + $request = $this->get_request_obj(); + $site = $request->addChild("site"); + $get = $site->addChild("get"); + $filter = $get->addChild("filter"); + + $filter->addChild("name", utf8_encode($domain)); + $dataset = $get->addChild("dataset"); + + $dataset->addChild("hosting"); + $packet = $request->asXML(); + + // send the request + $res = $this->send_request($packet); + + // make it to simple-xml-object + $xml = new SimpleXMLElement($res); + + return $xml; + } + + /** + * Get psa-id of a domain + * + * @param string $domain domain-name + * + * @returns bool|int false if failed and integer if successed + */ + function get_domain_id($domain) + { + $xml = $this->domain_info($domain); + $id = intval($xml->site->get->result->id); + $id = (is_int($id)) ? $id : false; + + return $id; + } + + /** + * Change Password of a mailbox + * + * @param string $mailbox full email-adress (user@domain.tld) + * @param string $newpass new password of mailbox + * + * @returns bool + */ + function change_mailbox_password($mailbox, $newpass) + { + list($user, $domain) = explode("@", $mailbox); + $domain_id = $this->get_domain_id($domain); + + // if domain cannot be resolved to an id, do not continue + if (!$domain_id) { + return false; + } + + // build xml-packet + $request = $this -> get_request_obj(); + $mail = $request -> addChild("mail"); + $update = $mail -> addChild("update"); + $add = $update -> addChild("set"); + $filter = $add -> addChild("filter"); + $filter->addChild("site-id", $domain_id); + + $mailname = $filter->addChild("mailname"); + $mailname->addChild("name", $user); + + $password = $mailname->addChild("password"); + $password->addChild("value", $newpass); + $password->addChild("type", "plain"); + + $packet = $request->asXML(); + + // send the request to plesk + $res = $this->send_request($packet); + $xml = new SimpleXMLElement($res); + $res = strval($xml->mail->update->set->result->status); + + return $res == "ok"; + } +} diff --git a/plugins/password/drivers/poppassd.php b/plugins/password/drivers/poppassd.php new file mode 100644 index 000000000..7a2821083 --- /dev/null +++ b/plugins/password/drivers/poppassd.php @@ -0,0 +1,82 @@ +<?php + +/** + * Poppassd Password Driver + * + * Driver to change passwords via Poppassd/Courierpassd + * + * @version 2.0 + * @author Philip Weir + * + * Copyright (C) 2005-2013, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_poppassd_password +{ + function format_error_result($code, $line) + { + if (preg_match('/^\d\d\d\s+(\S.*)\s*$/', $line, $matches)) { + return array('code' => $code, 'message' => $matches[1]); + } + + return $code; + } + + function save($curpass, $passwd) + { + $rcmail = rcmail::get_instance(); +// include('Net/Socket.php'); + $poppassd = new Net_Socket(); + + $result = $poppassd->connect($rcmail->config->get('password_pop_host'), $rcmail->config->get('password_pop_port'), null); + if (is_a($result, 'PEAR_Error')) { + return $this->format_error_result(PASSWORD_CONNECT_ERROR, $result->getMessage()); + } + else { + $result = $poppassd->readLine(); + if(!preg_match('/^2\d\d/', $result)) { + $poppassd->disconnect(); + return $this->format_error_result(PASSWORD_ERROR, $result); + } + else { + $poppassd->writeLine("user ". $_SESSION['username']); + $result = $poppassd->readLine(); + if (!preg_match('/^[23]\d\d/', $result) ) { + $poppassd->disconnect(); + return $this->format_error_result(PASSWORD_CONNECT_ERROR, $result); + } + else { + $poppassd->writeLine("pass ". $curpass); + $result = $poppassd->readLine(); + if (!preg_match('/^[23]\d\d/', $result) ) { + $poppassd->disconnect(); + return $this->format_error_result(PASSWORD_ERROR, $result); + } + else { + $poppassd->writeLine("newpass ". $passwd); + $result = $poppassd->readLine(); + $poppassd->disconnect(); + if (!preg_match('/^2\d\d/', $result)) { + return $this->format_error_result(PASSWORD_ERROR, $result); + } + + return PASSWORD_SUCCESS; + } + } + } + } + } +} diff --git a/plugins/password/drivers/pw_usermod.php b/plugins/password/drivers/pw_usermod.php new file mode 100644 index 000000000..c519bf4a4 --- /dev/null +++ b/plugins/password/drivers/pw_usermod.php @@ -0,0 +1,56 @@ +<?php + +/** + * pw_usermod Driver + * + * Driver that adds functionality to change the systems user password via + * the 'pw usermod' command. + * + * For installation instructions please read the README file. + * + * @version 2.0 + * @author Alex Cartwright <acartwright@mutinydesign.co.uk> + * @author Adamson Huang <adomputer@gmail.com> + * + * Copyright (C) 2005-2013, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_pw_usermod_password +{ + public function save($currpass, $newpass) + { + $username = $_SESSION['username']; + $cmd = rcmail::get_instance()->config->get('password_pw_usermod_cmd'); + $cmd .= " $username > /dev/null"; + + $handle = popen($cmd, "w"); + fwrite($handle, "$newpass\n"); + + if (pclose($handle) == 0) { + return PASSWORD_SUCCESS; + } + else { + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: Unable to execute $cmd" + ), true, false); + } + + return PASSWORD_ERROR; + } +} diff --git a/plugins/password/drivers/sasl.php b/plugins/password/drivers/sasl.php new file mode 100644 index 000000000..f3baef557 --- /dev/null +++ b/plugins/password/drivers/sasl.php @@ -0,0 +1,60 @@ +<?php + +/** + * SASL Password Driver + * + * Driver that adds functionality to change the users Cyrus/SASL password. + * The code is derrived from the Squirrelmail "Change SASL Password" Plugin + * by Galen Johnson. + * + * It only works with saslpasswd2 on the same host where Roundcube runs + * and requires shell access and gcc in order to compile the binary. + * + * For installation instructions please read the README file. + * + * @version 2.0 + * @author Thomas Bruederli + * + * Copyright (C) 2005-2013, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_sasl_password +{ + function save($currpass, $newpass) + { + $curdir = RCUBE_PLUGINS_DIR . 'password/helpers'; + $username = escapeshellcmd($_SESSION['username']); + $args = rcmail::get_instance()->config->get('password_saslpasswd_args', ''); + + if ($fh = popen("$curdir/chgsaslpasswd -p $args $username", 'w')) { + fwrite($fh, $newpass."\n"); + $code = pclose($fh); + + if ($code == 0) + return PASSWORD_SUCCESS; + } + else { + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: Unable to execute $curdir/chgsaslpasswd" + ), true, false); + } + + return PASSWORD_ERROR; + } +} diff --git a/plugins/password/drivers/smb.php b/plugins/password/drivers/smb.php new file mode 100644 index 000000000..3e34c79a1 --- /dev/null +++ b/plugins/password/drivers/smb.php @@ -0,0 +1,74 @@ +<?php + +/** + * smb Driver + * + * Driver that adds functionality to change the systems user password via + * the 'smbpasswd' command. + * + * For installation instructions please read the README file. + * + * @version 2.0 + * @author Andy Theuninck <gohanman@gmail.com) + * + * Based on chpasswd roundcubemail password driver by + * @author Alex Cartwright <acartwright@mutinydesign.co.uk) + * and smbpasswd horde passwd driver by + * @author Rene Lund Jensen <Rene@lundjensen.net> + * + * Configuration settings: + * password_smb_host => samba host (default: localhost) + * password_smb_cmd => smbpasswd binary (default: /usr/bin/smbpasswd) + * + * Copyright (C) 2005-2013, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_smb_password +{ + + public function save($currpass, $newpass) + { + $host = rcmail::get_instance()->config->get('password_smb_host','localhost'); + $bin = rcmail::get_instance()->config->get('password_smb_cmd','/usr/bin/smbpasswd'); + $username = $_SESSION['username']; + + $host = rcube_utils::parse_host($host); + $tmpfile = tempnam(sys_get_temp_dir(),'smb'); + $cmd = $bin . ' -r ' . $host . ' -s -U "' . $username . '" > ' . $tmpfile . ' 2>&1'; + $handle = @popen($cmd, 'w'); + + fputs($handle, $currpass."\n"); + fputs($handle, $newpass."\n"); + fputs($handle, $newpass."\n"); + @pclose($handle); + $res = file($tmpfile); + unlink($tmpfile); + + if (strstr($res[count($res) - 1], 'Password changed for user') !== false) { + return PASSWORD_SUCCESS; + } + else { + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: Unable to execute $cmd" + ), true, false); + } + + return PASSWORD_ERROR; + } +} diff --git a/plugins/password/drivers/sql.php b/plugins/password/drivers/sql.php new file mode 100644 index 000000000..37e162e22 --- /dev/null +++ b/plugins/password/drivers/sql.php @@ -0,0 +1,212 @@ +<?php + +/** + * SQL Password Driver + * + * Driver for passwords stored in SQL database + * + * @version 2.0 + * @author Aleksander 'A.L.E.C' Machniak <alec@alec.pl> + * + * Copyright (C) 2005-2013, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_sql_password +{ + function save($curpass, $passwd) + { + $rcmail = rcmail::get_instance(); + + if (!($sql = $rcmail->config->get('password_query'))) { + $sql = 'SELECT update_passwd(%c, %u)'; + } + + if ($dsn = $rcmail->config->get('password_db_dsn')) { + $db = rcube_db::factory($dsn, '', false); + $db->set_debug((bool)$rcmail->config->get('sql_debug')); + } + else { + $db = $rcmail->get_dbh(); + } + + if ($db->is_error()) { + return PASSWORD_ERROR; + } + + // crypted password + if (strpos($sql, '%c') !== FALSE) { + $salt = ''; + + if (!($crypt_hash = $rcmail->config->get('password_crypt_hash'))) { + if (CRYPT_MD5) + $crypt_hash = 'md5'; + else if (CRYPT_STD_DES) + $crypt_hash = 'des'; + } + + switch ($crypt_hash) { + case 'md5': + $len = 8; + $salt_hashindicator = '$1$'; + break; + case 'des': + $len = 2; + break; + case 'blowfish': + $cost = (int) $rcmail->config->get('password_blowfish_cost'); + $cost = $cost < 4 || $cost > 31 ? 12 : $cost; + $len = 22; + $salt_hashindicator = sprintf('$2a$%02d$', $cost); + break; + case 'sha256': + $len = 16; + $salt_hashindicator = '$5$'; + break; + case 'sha512': + $len = 16; + $salt_hashindicator = '$6$'; + break; + default: + return PASSWORD_CRYPT_ERROR; + } + + //Restrict the character set used as salt (#1488136) + $seedchars = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + for ($i = 0; $i < $len ; $i++) { + $salt .= $seedchars[rand(0, 63)]; + } + + $sql = str_replace('%c', $db->quote(crypt($passwd, $salt_hashindicator ? $salt_hashindicator .$salt.'$' : $salt)), $sql); + } + + // dovecotpw + if (strpos($sql, '%D') !== FALSE) { + if (!($dovecotpw = $rcmail->config->get('password_dovecotpw'))) + $dovecotpw = 'dovecotpw'; + if (!($method = $rcmail->config->get('password_dovecotpw_method'))) + $method = 'CRAM-MD5'; + + // use common temp dir + $tmp_dir = $rcmail->config->get('temp_dir'); + $tmpfile = tempnam($tmp_dir, 'roundcube-'); + + $pipe = popen("$dovecotpw -s '$method' > '$tmpfile'", "w"); + if (!$pipe) { + unlink($tmpfile); + return PASSWORD_CRYPT_ERROR; + } + else { + fwrite($pipe, $passwd . "\n", 1+strlen($passwd)); usleep(1000); + fwrite($pipe, $passwd . "\n", 1+strlen($passwd)); + pclose($pipe); + $newpass = trim(file_get_contents($tmpfile), "\n"); + if (!preg_match('/^\{' . $method . '\}/', $newpass)) { + return PASSWORD_CRYPT_ERROR; + } + if (!$rcmail->config->get('password_dovecotpw_with_method')) + $newpass = trim(str_replace('{' . $method . '}', '', $newpass)); + unlink($tmpfile); + } + $sql = str_replace('%D', $db->quote($newpass), $sql); + } + + // hashed passwords + if (preg_match('/%[n|q]/', $sql)) { + if (!extension_loaded('hash')) { + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: 'hash' extension not loaded!" + ), true, false); + + return PASSWORD_ERROR; + } + + if (!($hash_algo = strtolower($rcmail->config->get('password_hash_algorithm')))) { + $hash_algo = 'sha1'; + } + + $hash_passwd = hash($hash_algo, $passwd); + $hash_curpass = hash($hash_algo, $curpass); + + if ($rcmail->config->get('password_hash_base64')) { + $hash_passwd = base64_encode(pack('H*', $hash_passwd)); + $hash_curpass = base64_encode(pack('H*', $hash_curpass)); + } + + $sql = str_replace('%n', $db->quote($hash_passwd, 'text'), $sql); + $sql = str_replace('%q', $db->quote($hash_curpass, 'text'), $sql); + } + + // Handle clear text passwords securely (#1487034) + $sql_vars = array(); + if (preg_match_all('/%[p|o]/', $sql, $m)) { + foreach ($m[0] as $var) { + if ($var == '%p') { + $sql = preg_replace('/%p/', '?', $sql, 1); + $sql_vars[] = (string) $passwd; + } + else { // %o + $sql = preg_replace('/%o/', '?', $sql, 1); + $sql_vars[] = (string) $curpass; + } + } + } + + $local_part = $rcmail->user->get_username('local'); + $domain_part = $rcmail->user->get_username('domain'); + $username = $_SESSION['username']; + $host = $_SESSION['imap_host']; + + // convert domains to/from punnycode + if ($rcmail->config->get('password_idn_ascii')) { + $domain_part = rcube_utils::idn_to_ascii($domain_part); + $username = rcube_utils::idn_to_ascii($username); + $host = rcube_utils::idn_to_ascii($host); + } + else { + $domain_part = rcube_utils::idn_to_utf8($domain_part); + $username = rcube_utils::idn_to_utf8($username); + $host = rcube_utils::idn_to_utf8($host); + } + + // at least we should always have the local part + $sql = str_replace('%l', $db->quote($local_part, 'text'), $sql); + $sql = str_replace('%d', $db->quote($domain_part, 'text'), $sql); + $sql = str_replace('%u', $db->quote($username, 'text'), $sql); + $sql = str_replace('%h', $db->quote($host, 'text'), $sql); + + $res = $db->query($sql, $sql_vars); + + if (!$db->is_error()) { + if (strtolower(substr(trim($sql),0,6)) == 'select') { + if ($db->fetch_array($res)) { + return PASSWORD_SUCCESS; + } + } + else { + // This is the good case: 1 row updated + if ($db->affected_rows($res) == 1) + return PASSWORD_SUCCESS; + // @TODO: Some queries don't affect any rows + // Should we assume a success if there was no error? + } + } + + return PASSWORD_ERROR; + } +} diff --git a/plugins/password/drivers/virtualmin.php b/plugins/password/drivers/virtualmin.php new file mode 100644 index 000000000..3001ad9d0 --- /dev/null +++ b/plugins/password/drivers/virtualmin.php @@ -0,0 +1,94 @@ +<?php + +/** + * Virtualmin Password Driver + * + * Driver that adds functionality to change the users Virtualmin password. + * The code is derrived from the Squirrelmail "Change Cyrus/SASL Password" Plugin + * by Thomas Bruederli. + * + * It only works with virtualmin on the same host where Roundcube runs + * and requires shell access and gcc in order to compile the binary. + * + * @version 3.0 + * @author Martijn de Munnik + * + * Copyright (C) 2005-2013, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_virtualmin_password +{ + function save($currpass, $newpass) + { + $rcmail = rcmail::get_instance(); + $format = $rcmail->config->get('password_virtualmin_format', 0); + $username = $_SESSION['username']; + + switch ($format) { + case 1: // username%domain + $domain = substr(strrchr($username, "%"), 1); + break; + case 2: // username.domain (could be bogus) + $pieces = explode(".", $username); + $domain = $pieces[count($pieces)-2]. "." . end($pieces); + break; + case 3: // domain.username (could be bogus) + $pieces = explode(".", $username); + $domain = $pieces[0]. "." . $pieces[1]; + break; + case 4: // username-domain + $domain = substr(strrchr($username, "-"), 1); + break; + case 5: // domain-username + $domain = str_replace(strrchr($username, "-"), "", $username); + break; + case 6: // username_domain + $domain = substr(strrchr($username, "_"), 1); + break; + case 7: // domain_username + $pieces = explode("_", $username); + $domain = $pieces[0]; + break; + default: // username@domain + $domain = substr(strrchr($username, "@"), 1); + } + + if (!$domain) { + $domain = $rcmail->user->get_username('domain'); + } + + $username = escapeshellcmd($username); + $domain = escapeshellcmd($domain); + $newpass = escapeshellcmd($newpass); + $curdir = RCUBE_PLUGINS_DIR . 'password/helpers'; + + exec("$curdir/chgvirtualminpasswd modify-user --domain $domain --user $username --pass $newpass", $output, $returnvalue); + + if ($returnvalue == 0) { + return PASSWORD_SUCCESS; + } + else { + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: Unable to execute $curdir/chgvirtualminpasswd" + ), true, false); + } + + return PASSWORD_ERROR; + } +} diff --git a/plugins/password/drivers/vpopmaild.php b/plugins/password/drivers/vpopmaild.php new file mode 100644 index 000000000..bc0c8f9da --- /dev/null +++ b/plugins/password/drivers/vpopmaild.php @@ -0,0 +1,71 @@ +<?php + +/** + * vpopmail Password Driver + * + * Driver to change passwords via vpopmaild + * + * @version 2.0 + * @author Johannes Hessellund + * + * Copyright (C) 2005-2013, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_vpopmaild_password +{ + function save($curpass, $passwd) + { + $rcmail = rcmail::get_instance(); + $vpopmaild = new Net_Socket(); + $host = $rcmail->config->get('password_vpopmaild_host'); + $port = $rcmail->config->get('password_vpopmaild_port'); + + $result = $vpopmaild->connect($host, $port, null); + if (is_a($result, 'PEAR_Error')) { + return PASSWORD_CONNECT_ERROR; + } + + $vpopmaild->setTimeout($rcmail->config->get('password_vpopmaild_timeout'),0); + + $result = $vpopmaild->readLine(); + if(!preg_match('/^\+OK/', $result)) { + $vpopmaild->disconnect(); + return PASSWORD_CONNECT_ERROR; + } + + $vpopmaild->writeLine("slogin ". $_SESSION['username'] . " " . $curpass); + $result = $vpopmaild->readLine(); + + if(!preg_match('/^\+OK/', $result) ) { + $vpopmaild->writeLine("quit"); + $vpopmaild->disconnect(); + return PASSWORD_ERROR; + } + + $vpopmaild->writeLine("mod_user ". $_SESSION['username']); + $vpopmaild->writeLine("clear_text_password ". $passwd); + $vpopmaild->writeLine("."); + $result = $vpopmaild->readLine(); + $vpopmaild->writeLine("quit"); + $vpopmaild->disconnect(); + + if (!preg_match('/^\+OK/', $result)) { + return PASSWORD_ERROR; + } + + return PASSWORD_SUCCESS; + } +} diff --git a/plugins/password/drivers/ximss.php b/plugins/password/drivers/ximss.php new file mode 100644 index 000000000..54477f730 --- /dev/null +++ b/plugins/password/drivers/ximss.php @@ -0,0 +1,89 @@ +<?php +/** + * Communigate driver for the Password Plugin for Roundcube + * + * Tested with Communigate Pro 5.1.2 + * + * Configuration options: + * password_ximss_host - Host name of Communigate server + * password_ximss_port - XIMSS port on Communigate server + * + * References: + * http://www.communigate.com/WebGuide/XMLAPI.html + * + * @version 2.0 + * @author Erik Meitner <erik wanderings.us> + * + * Copyright (C) 2005-2013, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_ximss_password +{ + function save($pass, $newpass) + { + $rcmail = rcmail::get_instance(); + + $host = $rcmail->config->get('password_ximss_host'); + $port = $rcmail->config->get('password_ximss_port'); + $sock = stream_socket_client("tcp://$host:$port", $errno, $errstr, 30); + + if ($sock === FALSE) { + return PASSWORD_CONNECT_ERROR; + } + + // send all requests at once(pipelined) + fwrite( $sock, '<login id="A001" authData="'.$_SESSION['username'].'" password="'.$pass.'" />'."\0"); + fwrite( $sock, '<passwordModify id="A002" oldPassword="'.$pass.'" newPassword="'.$newpass.'" />'."\0"); + fwrite( $sock, '<bye id="A003" />'."\0"); + + //example responses + // <session id="A001" urlID="4815-vN2Txjkggy7gjHRD10jw" userName="user@example.com"/>\0 + // <response id="A001"/>\0 + // <response id="A002"/>\0 + // <response id="A003"/>\0 + // or an error: + // <response id="A001" errorText="incorrect password or account name" errorNum="515"/>\0 + + $responseblob = ''; + while (!feof($sock)) { + $responseblob .= fgets($sock, 1024); + } + + fclose($sock); + + foreach( explode( "\0",$responseblob) as $response ) { + $resp = simplexml_load_string("<xml>".$response."</xml>"); + + if( $resp->response[0]['id'] == 'A001' ) { + if( isset( $resp->response[0]['errorNum'] ) ) { + return PASSWORD_CONNECT_ERROR; + } + } + else if( $resp->response[0]['id'] == 'A002' ) { + if( isset( $resp->response[0]['errorNum'] )) { + return PASSWORD_ERROR; + } + } + else if( $resp->response[0]['id'] == 'A003' ) { + if( isset($resp->response[0]['errorNum'] )) { + //There was a problem during logout(This is probably harmless) + } + } + } //foreach + + return PASSWORD_SUCCESS; + } +} diff --git a/plugins/password/drivers/xmail.php b/plugins/password/drivers/xmail.php new file mode 100644 index 000000000..a7d00a279 --- /dev/null +++ b/plugins/password/drivers/xmail.php @@ -0,0 +1,119 @@ +<?php +/** + * XMail Password Driver + * + * Driver for XMail password + * + * @version 2.0 + * @author Helio Cavichiolo Jr <helio@hcsistemas.com.br> + * + * Setup xmail_host, xmail_user, xmail_pass and xmail_port into + * config.inc.php of password plugin as follows: + * + * $config['xmail_host'] = 'localhost'; + * $config['xmail_user'] = 'YourXmailControlUser'; + * $config['xmail_pass'] = 'YourXmailControlPass'; + * $config['xmail_port'] = 6017; + * + * Copyright (C) 2005-2013, The Roundcube Dev Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +class rcube_xmail_password +{ + function save($currpass, $newpass) + { + $rcmail = rcmail::get_instance(); + list($user,$domain) = explode('@', $_SESSION['username']); + + $xmail = new XMail; + + $xmail->hostname = $rcmail->config->get('xmail_host'); + $xmail->username = $rcmail->config->get('xmail_user'); + $xmail->password = $rcmail->config->get('xmail_pass'); + $xmail->port = $rcmail->config->get('xmail_port'); + + if (!$xmail->connect()) { + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: Unable to connect to mail server" + ), true, false); + return PASSWORD_CONNECT_ERROR; + } + else if (!$xmail->send("userpasswd\t".$domain."\t".$user."\t".$newpass."\n")) { + $xmail->close(); + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: Unable to change password" + ), true, false); + return PASSWORD_ERROR; + } + else { + $xmail->close(); + return PASSWORD_SUCCESS; + } + } +} + +class XMail { + var $socket; + var $hostname = 'localhost'; + var $username = 'xmail'; + var $password = ''; + var $port = 6017; + + function send($msg) + { + socket_write($this->socket,$msg); + if (substr(socket_read($this->socket, 512, PHP_BINARY_READ),0,1) != "+") { + return false; + } + return true; + } + + function connect() + { + $this->socket = socket_create(AF_INET, SOCK_STREAM, 0); + if ($this->socket < 0) + return false; + + $result = socket_connect($this->socket, $this->hostname, $this->port); + if ($result < 0) { + socket_close($this->socket); + return false; + } + + if (substr(socket_read($this->socket, 512, PHP_BINARY_READ),0,1) != "+") { + socket_close($this->socket); + return false; + } + + if (!$this->send("$this->username\t$this->password\n")) { + socket_close($this->socket); + return false; + } + return true; + } + + function close() + { + $this->send("quit\n"); + socket_close($this->socket); + } +} |