Source for file peardb.php

Documentation is available at peardb.php

  1. <?php
  2.  
  3. /**
  4.  * Change password PearDB backend
  5.  *
  6.  * @copyright 2005-2020 The SquirrelMail Project Team
  7.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  8.  * @version $Id: peardb.php 14845 2020-01-07 08:09:34Z pdontthink $
  9.  * @package plugins
  10.  * @subpackage change_password
  11.  */
  12.  
  13. /** load Pear DB.
  14.  * Global is needed because library must be loaded before configuration
  15.  * in order to use DB constants.
  16.  */
  17. global $cpw_peardb_detect;
  18. $cpw_peardb_detect=@include_once('DB.php');
  19.  
  20. /** declare configuration globals */
  21. global $cpw_peardb_dsn$cpw_peardb_connect_opts$cpw_peardb_table,
  22.  $cpw_peardb_uid_field$cpw_peardb_domain_field$cpw_peardb_passwd_field,
  23.  $cpw_peardb_crypted_passwd$cpw_peardb_debug;
  24.  
  25. /**
  26.  * Connection DSN.
  27.  * Any format supported by peardb
  28.  * @global mixed $cpw_peardb_dsn 
  29.  */
  30. $cpw_peardb_dsn='';
  31.  
  32. /**
  33.  * Pear DB connection options
  34.  * @global array $cpw_peardb_connect_opts 
  35.  */
  36. $cpw_peardb_connect_opts=array();
  37.  
  38. /**
  39.  * Table that stores user information
  40.  * @global string $cpw_peardb_table 
  41.  */
  42. $cpw_peardb_table='';
  43.  
  44. /**
  45.  * Field that stores user name
  46.  * @global string $cpw_peardb_uid_field 
  47.  */
  48. $cpw_peardb_uid_field='userid';
  49.  
  50. /**
  51.  * Field that stores domain part of username
  52.  * @global string $cpw_peardb_domain_field 
  53.  */
  54. $cpw_peardb_domain_field='';
  55.  
  56. /**
  57.  * Field that stores password
  58.  * @global string $cpw_peardb_passwd_field 
  59.  */
  60. $cpw_peardb_passwd_field='password';
  61.  
  62. /**
  63.  * Passwords are plaintext or encrypted
  64.  * @global boolean $cpw_peardb_crypted_passwd 
  65.  */
  66. $cpw_peardb_crypted_passwd=false;
  67.  
  68. /**
  69.  * Controls output debugging errors
  70.  * Error messages might contain login and password information.
  71.  * Don't enable on production systems.
  72.  * @global boolean $cpw_peardb_debug 
  73.  */
  74. $cpw_peardb_debug=false;
  75.  
  76. /** configuration overrides */
  77. if isset($cpw_peardb&& is_array($cpw_peardb&& !empty($cpw_peardb) ) {
  78.     if (isset($cpw_peardb['dsn']))
  79.         $cpw_peardb_dsn=$cpw_peardb['dsn'];
  80.     if (isset($cpw_peardb['connect_opts']))
  81.         $cpw_peardb_connect_opts=$cpw_peardb['connect_opts'];
  82.     if (isset($cpw_peardb['table']))
  83.         $cpw_peardb_table=$cpw_peardb['table'];
  84.     if (isset($cpw_peardb['uid_field']))
  85.         $cpw_peardb_uid_field=$cpw_peardb['uid_field'];
  86.     if (isset($cpw_peardb['domain_field']))
  87.         $cpw_peardb_domain_field=$cpw_peardb['domain_field'];
  88.     if (isset($cpw_peardb['password_field']))
  89.         $cpw_peardb_passwd_field=$cpw_peardb['password_field'];
  90.     if (isset($cpw_peardb['crypted_passwd']))
  91.         $cpw_peardb_crypted_passwd=true;
  92.     if (isset($cpw_peardb['debug']))
  93.         $cpw_peardb_debug=$cpw_peardb['debug'];
  94. }
  95.  
  96. /**
  97.  * Define here the name of your password changing function.
  98.  */
  99. global $squirrelmail_plugin_hooks;
  100. $squirrelmail_plugin_hooks['change_password_dochange']['peardb'=
  101.         'cpw_peardb_dochange';
  102. $squirrelmail_plugin_hooks['change_password_init']['peardb'=
  103.         'cpw_peardb_init';
  104.  
  105. /**
  106.  * Checks if configuration is correct
  107.  */
  108. function cpw_peardb_init({
  109.     global $oTemplate$cpw_peardb_detect$cpw_peardb_dsn$cpw_peardb_table;
  110.  
  111.     if ($cpw_peardb_detect{
  112.         error_box(_("Plugin is unable to use PHP Pear DB libraries. PHP Pear includes must be available in your PHP include_path setting."));
  113.         $oTemplate->display('footer.tpl');
  114.         exit();
  115.     }
  116.  
  117.     // Test required settings
  118.     if ((is_string($cpw_peardb_dsn&& trim($cpw_peardb_dsn)=='')
  119.         || trim($cpw_peardb_table)=='' {
  120.         error_box(_("Required change password backend configuration options are missing."));
  121.         $oTemplate->display('footer.tpl');
  122.         exit();
  123.     }
  124. }
  125.  
  126.  
  127. /**
  128.  * Changes password
  129.  * @param array data The username/curpw/newpw data.
  130.  * @return array Array of error messages.
  131.  */
  132. function cpw_peardb_dochange($data{
  133.         $cpw_peardb_crypted_passwd$domain;
  134.  
  135.     $username $data['username'];
  136.     $curpw $data['curpw'];
  137.     $newpw $data['newpw'];
  138.  
  139.     $msgs array();
  140.  
  141.     // split user and domain parts from username, if domain field is set and username looks like email.
  142.     if ($cpw_peardb_domain_field!='' && preg_match("/(.*)@(.*)/",$username,$match)) {
  143.         $user=$match[1];
  144.         $user_domain=$match[2];
  145.     else {
  146.         $user=$username;
  147.         $user_domain=$domain;
  148.     }
  149.  
  150.     // connect to database and make sure that table exists
  151.     $cpw_db DB::connect($cpw_peardb_dsn$cpw_peardb_connect_opts);
  152.     if (PEAR::isError($cpw_db)) {
  153.         array_push($msgs,sprintf(_("Connection error: %s"),sm_encode_html_special_chars($cpw_db->getMessage())));
  154.         if ($cpw_peardb_debug)
  155.             array_push($msgs,sm_encode_html_special_chars($cpw_db->getuserinfo()));
  156.         return $msgs;
  157.     }
  158.  
  159.     // get table information
  160.     $table_info $cpw_db->tableinfo($cpw_peardb_table);
  161.     if (PEAR::isError($table_info)) {
  162.         array_push($msgs,sprintf(_("Invalid table name: %s"),sm_encode_html_special_chars($cpw_peardb_table)));
  163.         $cpw_db->disconnect();
  164.         return $msgs;
  165.     }
  166.  
  167.     if (empty($table_info)) {
  168.         array_push($msgs,_("User table is empty."));
  169.         $cpw_db->disconnect();
  170.         return $msgs;
  171.     }
  172.  
  173.     $cpw_peardb_uid_check=false;
  174.     $cpw_peardb_passwd_check=false;
  175.     $cpw_peardb_domain_check=(($cpw_peardb_domain_field=='')true false);
  176.     foreach($table_info as $key => $field_data{
  177.         if ($field_data['name']==$cpw_peardb_uid_field)
  178.             $cpw_peardb_uid_check=true;
  179.         if ($field_data['name']==$cpw_peardb_passwd_field)
  180.             $cpw_peardb_passwd_check=true;
  181.         if ($cpw_peardb_domain_field!='' && $field_data['name']==$cpw_peardb_domain_field)
  182.             $cpw_peardb_domain_check=true;
  183.     }
  184.     if ($cpw_peardb_uid_check{
  185.         array_push($msgs,_("Invalid uid field."));
  186.     }
  187.     if ($cpw_peardb_passwd_check{
  188.         array_push($msgs,_("Invalid password field"));
  189.     }
  190.     if ($cpw_peardb_domain_check{
  191.         array_push($msgs,_("Invalid domain field"));
  192.     }
  193.     if (empty($msgs)) {
  194.         $cpw_db->disconnect();
  195.         return $msgs;
  196.     }
  197.  
  198.     // find user's entry
  199.     $query='SELECT'
  200.         .' '.$cpw_db->quoteIdentifier($cpw_peardb_uid_field)
  201.         .', '.$cpw_db->quoteIdentifier($cpw_peardb_passwd_field)
  202.         .(($cpw_peardb_domain_field!=''', '.$cpw_db->quoteIdentifier($cpw_peardb_domain_field):'')
  203.         .' FROM '.$cpw_db->quoteIdentifier($cpw_peardb_table)
  204.         .' WHERE '
  205.         .$cpw_db->quoteIdentifier($cpw_peardb_uid_field).'='.$cpw_db->quoteSmart($user)
  206.         .(($cpw_peardb_domain_field!=''?
  207.           ' AND '.$cpw_db->quoteIdentifier($cpw_peardb_domain_field).'='.$cpw_db->quoteSmart($user_domain):
  208.           '');
  209.     $cpw_res=$cpw_db->query($query);
  210.     if (PEAR::isError($cpw_res)) {
  211.         array_push($msgs,sprintf(_("Query failed: %s"),sm_encode_html_special_chars($cpw_res->getMessage())));
  212.         $cpw_db->disconnect();
  213.         return $msgs;
  214.     }
  215.  
  216.     // make sure that there is only one user.
  217.     if ($cpw_res->numRows()==0{
  218.         array_push($msgs,_("Unable to find user in user table."));
  219.         $cpw_db->disconnect();
  220.         return $msgs;
  221.     }
  222.  
  223.     if ($cpw_res->numRows()>1{
  224.         array_push($msgs,_("Too many matches found in user table."));
  225.         $cpw_db->disconnect();
  226.         return $msgs;
  227.     }
  228.  
  229.     // FIXME: process possible errors
  230.     $cpw_res->fetchInto($userdb,DB_FETCHMODE_ASSOC);
  231.  
  232.     // validate password
  233.     $valid_passwd=false;
  234.     if ($cpw_peardb_crypted_passwd{
  235.         // detect password type
  236.         $pw_type=cpw_peardb_detect_crypto($userdb[$cpw_peardb_passwd_field]);
  237.         if ($pw_type{
  238.             array_push($msgs,_("Unable to detect password crypto algorithm."));
  239.         else {
  240.             $hashed_pw=cpw_peardb_passwd_hash($curpw,$pw_type,$msgs,$userdb[$cpw_peardb_passwd_field]);
  241.             if ($hashed_pw==$userdb[$cpw_peardb_passwd_field]{
  242.                 $valid_passwd=true;
  243.             }
  244.         }
  245.     elseif ($userdb[$cpw_peardb_passwd_field]==$curpw{
  246.         $valid_passwd=true;
  247.     }
  248.  
  249.     if ($valid_passwd{
  250.         array_push($msgs,CPW_CURRENT_NOMATCH);
  251.         $cpw_db->disconnect();
  252.         return $msgs;
  253.     }
  254.  
  255.     // create new password
  256.     if ($cpw_peardb_crypted_passwd{
  257.         $hashed_passwd=cpw_peardb_passwd_hash($newpw,$pw_type,$msgs);
  258.     else {
  259.         $hashed_passwd=$newpw;
  260.     }
  261.  
  262.     // make sure that password was created
  263.     if (empty($msgs)) {
  264.         array_push($msgs,_("Unable to encrypt new password."));
  265.         $cpw_db->disconnect();
  266.         return $msgs;
  267.     }
  268.  
  269.     // create update query
  270.     $update_query='UPDATE '
  271.         . $cpw_db->quoteIdentifier($cpw_peardb_table)
  272.         .' SET '.$cpw_db->quoteIdentifier($cpw_peardb_passwd_field)
  273.         .'='.$cpw_db->quoteSmart($hashed_passwd)
  274.         .' WHERE '.$cpw_db->quoteIdentifier($cpw_peardb_uid_field)
  275.         .'='.$cpw_db->quoteSmart($user)
  276.         .(($cpw_peardb_domain_field!=''?
  277.           ' AND '.$cpw_db->quoteIdentifier($cpw_peardb_domain_field).'='.$cpw_db->quoteSmart($user_domain:
  278.           '');
  279.  
  280.     // update database
  281.     $cpw_res=$cpw_db->query($update_query);
  282.  
  283.     // check for update error
  284.     if (PEAR::isError($cpw_res)) {
  285.         array_push($msgs,sprintf(_("Unable to set new password: %s"),sm_encode_html_special_chars($cpw_res->getMessage())));
  286.     }
  287.  
  288.     // close database connection
  289.     $cpw_db->disconnect();
  290.  
  291.     return $msgs;
  292. }
  293.  
  294. /**
  295.  * Detects password crypto
  296.  * reports 'crypt' if fails to detect any other crypt
  297.  * @param string $password 
  298.  * @return string 
  299.  */
  300. function cpw_peardb_detect_crypto($password{
  301.     $ret false;
  302.  
  303.     if (preg_match("/^\{(.+)\}+/",$password,$crypto)) {
  304.         $ret=strtolower($crypto[1]);
  305.     }
  306.  
  307.     if ($ret=='crypt'{
  308.         // {CRYPT} can be standard des crypt, extended des crypt, md5 crypt or blowfish
  309.         // depends on first salt symbols (ext_des = '_', md5 = '$1$', blowfish = '$2')
  310.         // and length of salt (des = 2 chars, ext_des = 9, md5 = 12, blowfish = 16).
  311.         if (preg_match("/^\{crypt\}\\\$1\\\$+/i",$password)) {
  312.             $ret='tagged_md5crypt';
  313.         elseif (preg_match("/^\{crypt\}\\\$2+/i",$password)) {
  314.             $ret='tagged_blowfish';
  315.         elseif (preg_match("/^\{crypt\}_+/i",$password)) {
  316.             $ret='tagged_extcrypt';
  317.         else {
  318.             $ret='tagged_crypt';
  319.         }
  320.     }
  321.  
  322.     if ($ret{
  323.         if (preg_match("/^\\\$1\\\$+/i",$password)) {
  324.             $ret='md5crypt';
  325.         elseif (preg_match("/^\\\$2+/i",$password)) {
  326.             $ret='blowfish';
  327.         elseif (preg_match("/^_+/i",$password)) {
  328.             $ret='extcrypt';
  329.         else {
  330.             $ret='crypt';
  331.         }
  332.     }
  333.     return $ret;
  334. }
  335.  
  336. /**
  337.  * Encode password
  338.  * @param string $password plain text password
  339.  * @param string $crypto used crypto
  340.  * @param array $msgs error messages
  341.  * @param string $forced_salt old password used to create password hash for verification
  342.  * @return string hashed password. false, if hashing fails
  343.  */
  344. function cpw_peardb_passwd_hash($password,$crypto,&$msgs,$forced_salt=''{
  345.     global $username;
  346.  
  347.     $crypto strtolower($crypto);
  348.  
  349.     $ret=false;
  350.     $salt='';
  351.     // extra symbols used for random string in crypt salt
  352.     // squirrelmail GenerateRandomString() adds alphanumerics with third argument = 7.
  353.     $extra_salt_chars='./';
  354.  
  355.     switch($crypto{
  356.     case 'plain-md5':
  357.         $ret='{PLAIN-MD5}' md5($password);
  358.         break;
  359.     case 'digest-md5':
  360.         // split username into user and domain parts
  361.         if (preg_match("/(.*)@(.*)/",$username,$match)) {
  362.             $ret='{DIGEST-MD5}' md5($match[1].':'.$match[2].':'.$password);
  363.         else {
  364.             array_push($msgs,_("Unable to use digest-md5 crypto."));
  365.         }
  366.         break;
  367.     case 'tagged_crypt':
  368.     case 'crypt':
  369.         if (defined('CRYPT_STD_DES'|| CRYPT_STD_DES==0{
  370.             array_push($msgs,sprintf(_("Unsupported crypto: %s"),'crypt'));
  371.             break;
  372.         }
  373.         if ($forced_salt==''{
  374.             $salt=GenerateRandomString(2,$extra_salt_chars,7);
  375.         else {
  376.             $salt=$forced_salt;
  377.         }
  378.         $ret ($crypto=='tagged_crypt' '{crypt}' '');
  379.         $ret.= crypt($password,$salt);
  380.         break;
  381.     case 'tagged_md5crypt':
  382.     case 'md5crypt':
  383.         if (defined('CRYPT_MD5'|| CRYPT_MD5==0{
  384.             array_push($msgs,sprintf(_("Unsupported crypto: %s"),'md5crypt'));
  385.             break;
  386.         }
  387.         if ($forced_salt==''{
  388.             $salt='$1$' .GenerateRandomString(9,$extra_salt_chars,7);
  389.         else {
  390.             $salt=$forced_salt;
  391.         }
  392.         $ret ($crypto=='tagged_md5crypt' '{crypt}' '');
  393.         $ret.= crypt($password,$salt);
  394.         break;
  395.     case 'tagged_extcrypt':
  396.     case 'extcrypt':
  397.         if (defined('CRYPT_EXT_DES'|| CRYPT_EXT_DES==0{
  398.             array_push($msgs,sprintf(_("Unsupported crypto: %s"),'extcrypt'));
  399.             break;
  400.         }
  401.         if ($forced_salt==''{
  402.             $salt='_' GenerateRandomString(8,$extra_salt_chars,7);
  403.         else {
  404.             $salt=$forced_salt;
  405.         }
  406.         $ret ($crypto=='tagged_extcrypt' '{crypt}' '');
  407.         $ret.= crypt($password,$salt);
  408.         break;
  409.     case 'tagged_blowfish':
  410.     case 'blowfish':
  411.         if (defined('CRYPT_BLOWFISH'|| CRYPT_BLOWFISH==0{
  412.             array_push($msgs,sprintf(_("Unsupported crypto: %s"),'blowfish'));
  413.             break;
  414.         }
  415.         if ($forced_salt==''{
  416.             $salt='$2a$12$' GenerateRandomString(13,$extra_salt_chars,7);
  417.         else {
  418.             $salt=$forced_salt;
  419.         }
  420.         $ret ($crypto=='tagged_blowfish' '{crypt}' '');
  421.         $ret.= crypt($password,$salt);
  422.         break;
  423.     case 'plain':
  424.     case 'plaintext':
  425.         $ret $password;
  426.         break;
  427.     default:
  428.         array_push($msgs,sprintf(_("Unsupported crypto: %s"),sm_encode_html_special_chars($crypto)));
  429.     }
  430.     return $ret;
  431. }

Documentation generated on Mon, 13 Jan 2020 04:23:20 +0100 by phpDocumentor 1.4.3