Changeset 1304

Show
Ignore:
Timestamp:
10/22/07 15:06:20 (6 years ago)
Author:
benoitg
Message:
  • Major security fix: Fix the authenticator for a security breach where a user could get Internet access using an empty username. LocalUser? and LDAP were definitely vulnerable, RADIUS may have been.
Location:
trunk/wifidog-auth
Files:
7 modified

Legend:

Unmodified
Added
Removed
  • trunk/wifidog-auth/CHANGELOG

    r1301 r1304  
    11# $Id$ 
     22007-10-22 Benoit Grégoire  <bock@step.polymtl.ca> 
     3        * Major security fix:  Fix the authenticator for a security breach where a user could get Internet access using an empty username.  LocalUser and LDAP were definitely vulnerable, RADIUS may have been. 
     4         
    252007-10-01 Benoit Grégoire  <bock@step.polymtl.ca> 
    36        * Remove Array type hinting in Security.php. It's not supported in PHP < 5.1.  Fixes  #393 
  • trunk/wifidog-auth/wifidog/classes/Authenticator.php

    r1289 r1304  
    140140            $selectedUser=null; 
    141141        } 
    142          
     142 
    143143        $smarty=SmartyWiFiDog::getObject(); 
    144144        // Set network selector 
     
    163163    { 
    164164        if (!empty($_REQUEST["login_form_submit"])) { 
    165                     if (isset($_REQUEST["user_id"])) { 
     165            if (isset($_REQUEST["user_id"])) { 
    166166                $username = User::getObject($_REQUEST["user_id"])->getUsername(); 
    167167            } 
     
    173173                $password = $_REQUEST["password"]; 
    174174            } 
    175              
     175 
    176176            // Authenticating the user through the selected auth source. 
    177177            $network = Network::processSelectUI('auth_source'); 
  • trunk/wifidog-auth/wifidog/classes/Authenticators/AuthenticatorLDAP.php

    r1249 r1304  
    125125     * @return void 
    126126     */ 
    127         public function __construct($account_orgin, $host, $rdn, $pass, $o, $filter) 
     127    public function __construct($account_orgin, $host, $rdn, $pass, $o, $filter) 
    128128    { 
    129129        // Call parent constructor 
    130130        parent::__construct($account_orgin); 
    131131 
    132                 $this->mldap_hostname = $host; 
    133                 $this->mldap_filter = $filter; 
    134                 $this->mldap_o = $o; 
    135                 $this->mldap_rdn = trim($rdn); 
    136                 $this->mldap_pass = trim($pass); 
     132        $this->mldap_hostname = $host; 
     133        $this->mldap_filter = $filter; 
     134        $this->mldap_o = $o; 
     135        $this->mldap_rdn = trim($rdn); 
     136        $this->mldap_pass = trim($pass); 
    137137    } 
    138138 
     
    151151 
    152152     */ 
    153         private function checkLdapUser($username, $password, $ldap_server, $o, $f, &$errmsg = null ) 
    154         { 
    155             // Init values 
    156                 $rtval = true; 
    157  
    158                 // Check if php-ldap extension is loaded 
    159                 if (Dependency::check("ldap", $errmsg)) { 
    160                 if ($connect = @ldap_connect($ldap_server)) { 
    161                     // if connected to ldap server 
    162                         ldap_set_option($connect, LDAP_OPT_PROTOCOL_VERSION, 3); 
    163  
    164                         // bind to ldap connection 
     153    private function checkLdapUser($username, $password, $ldap_server, $o, $f, &$errmsg = null ) 
     154    { 
     155        // Init values 
     156        $rtval = true; 
     157 
     158        // Check if php-ldap extension is loaded 
     159        if (Dependency::check("ldap", $errmsg)) { 
     160            if ($connect = @ldap_connect($ldap_server)) { 
     161                // if connected to ldap server 
     162                ldap_set_option($connect, LDAP_OPT_PROTOCOL_VERSION, 3); 
     163 
     164                // bind to ldap connection 
    165165                if (strlen(trim($this->mldap_rdn)) == 0) { 
    166                                 if (($bind = @ldap_bind($connect)) == false) { 
    167                                         $errmsg = _("Error while connecting to the LDAP server."); 
    168                                         return false; 
    169                                 } 
    170                         } else { 
    171                                 if (($bind = @ldap_bind($connect, $this->mldap_rdn, $this->mldap_pass )) == false) { 
    172                                         $errmsg = _("Error while connecting to the LDAP server."); 
    173                                         return false; 
    174                                 } 
    175                         } 
    176  
    177                         // search for user 
    178                         if (($res_id = ldap_search($connect, "o=$o", "$f=$username")) == false)  { 
    179                                 $errmsg = _("Error while obtaining your LDAP information."); 
    180  
    181                                 return false; 
    182                         } 
    183  
    184                         if (ldap_count_entries($connect, $res_id) != 1) { 
    185                                 $errmsg = _("Error while obtaining your username or password from the LDAP server."); 
    186  
    187                                 return false; 
    188                         } 
    189  
    190                         if (($entry_id = ldap_first_entry($connect, $res_id)) == false) { 
    191                                 $errmsg = _("Error while obtaining your username or password from the LDAP server."); 
    192  
    193                                 return false; 
    194                         } 
    195  
    196                         if (($user_dn = ldap_get_dn($connect, $entry_id)) == false) { 
    197                                 $errmsg = _("Error while obtaining your username or password from the LDAP server."); 
    198  
    199                                 return false; 
    200                         } 
    201  
    202                         //Authenticate the User 
    203                         if (($link_id = ldap_bind($connect, $user_dn, $password)) == false) { 
    204                                 $errmsg = _("Error in username or password."); 
    205  
    206                                 return false; 
    207                         } 
    208  
    209                         return true; 
    210                 } else { 
    211                         $errmsg = _("Error connecting to the LDAP Server."); 
    212                 } 
    213  
    214                 ldap_close($connect); 
    215                 } else { 
    216                     $rtval = false; 
    217                 } 
    218         } 
     166                    if (($bind = @ldap_bind($connect)) == false) { 
     167                        $errmsg = _("Error while connecting to the LDAP server."); 
     168                        return false; 
     169                    } 
     170                } else { 
     171                    if (($bind = @ldap_bind($connect, $this->mldap_rdn, $this->mldap_pass )) == false) { 
     172                        $errmsg = _("Error while connecting to the LDAP server."); 
     173                        return false; 
     174                    } 
     175                } 
     176 
     177                // search for user 
     178                if (($res_id = ldap_search($connect, "o=$o", "$f=$username")) == false)  { 
     179                    $errmsg = _("Error while obtaining your LDAP information."); 
     180 
     181                    return false; 
     182                } 
     183 
     184                if (ldap_count_entries($connect, $res_id) != 1) { 
     185                    $errmsg = _("Error while obtaining your username or password from the LDAP server."); 
     186 
     187                    return false; 
     188                } 
     189 
     190                if (($entry_id = ldap_first_entry($connect, $res_id)) == false) { 
     191                    $errmsg = _("Error while obtaining your username or password from the LDAP server."); 
     192 
     193                    return false; 
     194                } 
     195 
     196                if (($user_dn = ldap_get_dn($connect, $entry_id)) == false) { 
     197                    $errmsg = _("Error while obtaining your username or password from the LDAP server."); 
     198 
     199                    return false; 
     200                } 
     201 
     202                //Authenticate the User 
     203                if (($link_id = ldap_bind($connect, $user_dn, $password)) == false) { 
     204                    $errmsg = _("Error in username or password."); 
     205 
     206                    return false; 
     207                } 
     208 
     209                return true; 
     210            } else { 
     211                $errmsg = _("Error connecting to the LDAP Server."); 
     212            } 
     213 
     214            ldap_close($connect); 
     215        } else { 
     216            $rtval = false; 
     217        } 
     218    } 
    219219 
    220220    /** 
     
    231231     *                otherwise. 
    232232     */ 
    233         public function login($username, $password, &$errmsg = null) 
    234         { 
    235              
    236                 $db = AbstractDb::getObject(); 
    237  
    238                 // Init values 
    239                 $retval = false; 
    240                 $username = $db->EscapeString($username); 
    241                 $password = $db->EscapeString($password); 
    242  
    243                 // Check if php-ldap extension is loaded 
    244                 if (Dependency::check("ldap", $errmsg)) { 
    245                 if ($this->checkLdapUser($username, $password, $this->mldap_hostname, $this->mldap_o, $this->mldap_filter, $errmsg)) { 
    246                         //LDAP Authentication Successful 
    247                         $sql = "SELECT user_id, pass FROM users WHERE (username='$username') AND account_origin='".$this->getNetwork()->getId()."'"; 
    248  
    249                         $db->ExecSqlUniqueRes($sql, $user_info, false); 
    250  
    251                         if ($user_info != null) { 
    252                                 $user = User::getObject($user_info['user_id']); 
    253  
    254                                 if ($user->isUserValid($errmsg)) { 
    255                                         $retval = $user; 
    256                                         User::setCurrentUser($user); 
    257                                         $errmsg = _("Login successfull"); 
    258                                 } else { 
    259                                         $retval = false; 
    260                                 //Error already been set 
    261                                 } 
    262                         } else { 
    263                                 $user = User::createUser(get_guid(), $username, $this->getNetwork(), "", ""); 
    264                                 $retval = &$user; 
    265                                 $user->setAccountStatus(ACCOUNT_STATUS_ALLOWED); 
    266                                 User::setCurrentUser($user); 
    267                                 $errmsg = _("Login successfull"); 
    268                         } 
    269                 } else { 
    270                         return false; 
    271                         //Error already been set 
    272                 } 
    273                 } 
    274  
    275                 return $retval; 
    276         } 
     233    public function login($username, $password, &$errmsg = null) 
     234    { 
     235          
     236        $db = AbstractDb::getObject(); 
     237 
     238        // Init values 
     239        $retval = false; 
     240        $username = $db->EscapeString($username); 
     241        $password = $db->EscapeString($password); 
     242 
     243        // Check if php-ldap extension is loaded 
     244        if (Dependency::check("ldap", $errmsg)) { 
     245            if ($this->checkLdapUser($username, $password, $this->mldap_hostname, $this->mldap_o, $this->mldap_filter, $errmsg)) { 
     246                //LDAP Authentication Successful 
     247                $sql = "SELECT user_id, pass FROM users WHERE (username='$username') AND account_origin='".$this->getNetwork()->getId()."'"; 
     248 
     249                $db->ExecSqlUniqueRes($sql, $user_info, false); 
     250 
     251                if ($user_info != null) { 
     252                    $user = User::getObject($user_info['user_id']); 
     253 
     254                    if ($user->isUserValid($errmsg)) { 
     255                        $retval = $user; 
     256                        User::setCurrentUser($user); 
     257                        $errmsg = _("Login successfull"); 
     258                    } else { 
     259                        $retval = false; 
     260                        //Error already been set 
     261                    } 
     262                } else { 
     263                    $user = User::createUser(get_guid(), $username, $this->getNetwork(), "", ""); 
     264                    $retval = &$user; 
     265                    $user->setAccountStatus(ACCOUNT_STATUS_ALLOWED); 
     266 
     267                    $errmsg = _("Login successfull"); 
     268                } 
     269            } else { 
     270                $retval = false; 
     271                //Error already been set 
     272            } 
     273        } 
     274        User::setCurrentUser($retval); 
     275        return $retval; 
     276    } 
    277277 
    278278    /** 
  • trunk/wifidog-auth/wifidog/classes/Authenticators/AuthenticatorLocalUser.php

    r1249 r1304  
    107107    public function login($username, $password, &$errmsg = null) 
    108108    { 
    109          
     109        //echo "DEBUG:  login($username, $password, $errmsg)<br/>"; 
    110110        $db = AbstractDb::getObject(); 
    111111 
     
    114114 
    115115        $username = $db->escapeString($username); 
    116         $password = $db->escapeString($password); 
    117         $password_hash = User::passwordHash($_REQUEST['password']); 
    118  
    119         $sql = "SELECT user_id FROM users WHERE (username='$username' OR email='$username') AND account_origin='".$this->getNetwork()->getId()."' AND pass='$password_hash'"; 
    120         $db->execSqlUniqueRes($sql, $user_info, false); 
    121  
    122         if ($user_info != null) { 
    123             $user = User::getObject($user_info['user_id']); 
    124  
    125             if ($user->isUserValid($errmsg)) { 
    126                 $retval = &$user; 
    127                 User::setCurrentUser($user); 
    128                 $errmsg = _("Login successfull"); 
    129             } else { 
    130                 $retval = false; 
    131                 //Reason for refusal is already in $errmsg 
    132             } 
    133         } else { 
    134             /* 
    135              * This is only used to discriminate if the problem was a 
    136              * non-existent user of a wrong password. 
    137              */ 
    138             $user_info = null; 
    139             $db->execSqlUniqueRes("SELECT * FROM users WHERE (username='$username' OR email='$username') AND account_origin='".$this->getNetwork()->getId()."'", $user_info, false); 
    140  
    141             if ($user_info == null) { 
    142                 $errmsg = _('Unknown username or email'); 
    143             } else { 
    144                 $errmsg = _('Incorrect password (Maybe you have CAPS LOCK on?)'); 
    145             } 
    146  
     116        if (empty($username)) { 
     117            $errmsg .= sprintf(_("Fatal error:  Username cannot be empty")); 
    147118            $retval = false; 
    148119        } 
    149  
     120        else{ 
     121            $password = $db->escapeString($password); 
     122            $password_hash = User::passwordHash($_REQUEST['password']); 
     123 
     124            $sql = "SELECT user_id FROM users WHERE (username='$username' OR email='$username') AND account_origin='".$this->getNetwork()->getId()."' AND pass='$password_hash'"; 
     125            $db->execSqlUniqueRes($sql, $user_info, false); 
     126 
     127            if ($user_info != null) { 
     128                $user = User::getObject($user_info['user_id']); 
     129 
     130                if ($user->isUserValid($errmsg)) { 
     131                    $retval = &$user; 
     132                    $errmsg = _("Login successfull"); 
     133                } else { 
     134                    $retval = false; 
     135                    //Reason for refusal is already in $errmsg 
     136                } 
     137            } else { 
     138                /* 
     139                 * This is only used to discriminate if the problem was a 
     140                 * non-existent user of a wrong password. 
     141                 */ 
     142                $user_info = null; 
     143                $db->execSqlUniqueRes("SELECT * FROM users WHERE (username='$username' OR email='$username') AND account_origin='".$this->getNetwork()->getId()."'", $user_info, false); 
     144 
     145                if ($user_info == null) { 
     146                    $errmsg = _('Unknown username or email'); 
     147                } else { 
     148                    $errmsg = _('Incorrect password (Maybe you have CAPS LOCK on?)'); 
     149                } 
     150 
     151                $retval = false; 
     152            } 
     153        } 
     154        User::setCurrentUser($retval); 
    150155        return $retval; 
    151156    } 
  • trunk/wifidog-auth/wifidog/classes/Authenticators/AuthenticatorRadius.php

    r1249 r1304  
    174174         
    175175        $db = AbstractDb::getObject(); 
    176  
     176        User :: setCurrentUser(null);//This should fix a security hole if using an empty username.  I didn't have time to audit the radius code to see if it really was vulnerable, and code a better fix. 
    177177        // Init values 
    178178        $retval = false; 
  • trunk/wifidog-auth/wifidog/classes/User.php

    r1289 r1304  
    121121     * This should NOT be called by anything except the Authenticators 
    122122     * 
    123      * @param object $user User a user object 
     123     * @param object $user User a user object, or null 
    124124     * 
    125125     * @return bool True if everything went well setting the session 
    126126 
    127127     */ 
    128     public static function setCurrentUser(User $user) { 
     128    public static function setCurrentUser($user) { 
     129 
     130        if (get_class($user) == 'User'){ 
     131            $userId = $user->getId(); 
     132            $passwordHash = $user->getPasswordHash(); 
     133        } 
     134        else { 
     135            $userId = null; 
     136            $passwordHash = null; 
     137        } 
     138 
    129139        try { 
    130140            $session = Session::getObject(); 
    131             $session->set(SESS_USER_ID_VAR, $user->getId()); 
    132             $session->set(SESS_PASSWORD_HASH_VAR, $user->getPasswordHash()); 
     141            $session->set(SESS_USER_ID_VAR, $userId); 
     142            $session->set(SESS_PASSWORD_HASH_VAR, $passwordHash); 
    133143            return true; 
    134144        } catch (Exception $e) { 
     
    330340        return $this->_row['open_id_url']; 
    331341    } 
    332      
     342 
    333343    function getUsername() { 
    334344        return $this->_row['username']; 
     
    936946            'title' => _("Import NoCat user database"), 
    937947            'url' => BASE_URL_PATH."admin/import_user_database.php" 
    938                 ); 
    939         } 
    940             if(Security::getObjectsWithPermission(Permission::P('NETWORK_PERM_EDIT_ANY_USER'))) 
     948            ); 
     949        } 
     950        if(Security::getObjectsWithPermission(Permission::P('NETWORK_PERM_EDIT_ANY_USER'))) 
    941951        { 
    942952            $items[] = array('path' => 'users/user_manager', 
    943953            'title' => _("User manager"), 
    944954            'url' => BASE_URL_PATH."admin/user_log.php" 
    945                 ); 
    946         } 
    947             if(Security::getObjectsWithPermission(Permission::P('NETWORK_PERM_VIEW_STATISTICS'))) 
     955            ); 
     956        } 
     957        if(Security::getObjectsWithPermission(Permission::P('NETWORK_PERM_VIEW_STATISTICS'))) 
    948958        { 
    949959            $items[] = array('path' => 'users/statistics', 
    950960            'title' => _("Statistics"), 
    951961            'url' => BASE_URL_PATH."admin/stats.php" 
    952                 ); 
     962            ); 
    953963        } 
    954964        $items[] = array('path' => 'users', 
  • trunk/wifidog-auth/wifidog/login/index.php

    r1261 r1304  
    199199    $user = User::getCurrentUser(); 
    200200    if (!$user) { 
    201         //Normally, we already have a user logged-in (precessed by process_login_out.php).  But we try again, if only to display the error 
     201        //Normally, we already have a user logged-in (processed by process_login_out.php).  But we try again, if only to display the error 
    202202        Authenticator::processLoginUI($errmsg); 
    203203    } 
    204  
     204//echo "DEBUG: user: "; echo $user->getUsername(); 
    205205    if ($user != null) { 
    206206        if (!empty($gw_address) && !empty($gw_port)) {