Quoting Chuck Hagenbuch <[EMAIL PROTECTED]>:

I agree it would be nice, but that's more in the realm of an
enhancement than a security fix. We'll consider it for Turba 2.2, but
I'd like to get 2.1.7 out with the fixes now.

Finally, these should be the patches for the upcoming Turba 2.1.7 and Turba 2.2-RC3 releases. I plan to roll them tomorrow (Friday) morning, U.S Eastern time. I'm also attaching a patch for HEAD for anyone who wants/needs it.

Thanks to Peter, and also Michael R. for the count checks.

-chuck
? urls
Index: docs/CHANGES
===================================================================
RCS file: /repository/turba/docs/CHANGES,v
retrieving revision 1.384
diff -u -r1.384 CHANGES
--- docs/CHANGES	10 Feb 2008 16:01:44 -0000	1.384
+++ docs/CHANGES	15 Feb 2008 05:56:34 -0000
@@ -9,6 +9,8 @@
 v2.2-cvs
 --------
 
+[cjh] SECURITY: Fix unchecked access to contacts in the same SQL table
+      (Bug #6208).
 [jan] Add configuration to more flexibly parse full names into the name parts.
 
 
Index: lib/Driver.php
===================================================================
RCS file: /repository/turba/lib/Driver.php,v
retrieving revision 1.181
diff -u -r1.181 Driver.php
--- lib/Driver.php	11 Feb 2008 00:40:39 -0000	1.181
+++ lib/Driver.php	15 Feb 2008 05:56:34 -0000
@@ -618,9 +618,8 @@
      */
     function &getObjects($objectIds)
     {
-        $criteria = $this->map['__key'];
-
-        $objects = $this->_read($criteria, $objectIds,
+        $objects = $this->_read($this->map['__key'], $objectIds,
+                                $this->getContactOwner(),
                                 array_values($this->fields));
         if (is_a($objects, 'PEAR_Error')) {
             return $objects;
@@ -1573,22 +1572,22 @@
     }
 
     /**
-     * Reads the given data from the address book and returns the result's
-     * fields.
+     * Reads the given data from the address book and returns the results.
      *
-     * @param array $criteria  Search criteria.
-     * @param string $id       Data identifier.
-     * @param array $fields    List of fields to return.
+     * @param string $key    The primary key field to use.
+     * @param mixed $ids     The ids of the contacts to load.
+     * @param string $owner  Only return contacts owned by this user.
+     * @param array $fields  List of fields to return.
      *
-     * @return  Hash containing the search results.
+     * @return array  Hash containing the search results.
      */
-    function _read($criteria, $id, $fields)
+    function _read($key, $ids, $owner, $fields)
     {
         return PEAR::raiseError(_("Reading contacts is not available."));
     }
 
     /**
-     * Adds the specified object to the SQL database.
+     * Adds the specified contact to the SQL database.
      */
     function _add($attributes)
     {
@@ -1596,7 +1595,7 @@
     }
 
     /**
-     * Deletes the specified object from the SQL database.
+     * Deletes the specified contact from the SQL database.
      */
     function _delete($object_key, $object_id)
     {
Index: lib/Driver/imsp.php
===================================================================
RCS file: /repository/turba/lib/Driver/imsp.php,v
retrieving revision 1.61
diff -u -r1.61 imsp.php
--- lib/Driver/imsp.php	4 Jan 2008 18:53:28 -0000	1.61
+++ lib/Driver/imsp.php	15 Feb 2008 05:56:34 -0000
@@ -131,7 +131,7 @@
         }
 
         /* Now we have a list of names, get the rest. */
-        $result = $this->_read('name', $names, $fields);
+        $result = $this->_read('name', $names, null, $fields);
         if (is_array($result)) {
             $results = $result;
         }
@@ -143,32 +143,33 @@
 
     /**
      * Reads the given data from the IMSP server and returns the
-     * result's fields.
+     * results.
      *
-     * @param array $criteria  (Ignored: Always 'name' for IMSP) Search criteria.
-     * @param array $id        Array of data identifiers.
-     * @param array $fields    List of fields to return.
+     * @param string $key    The primary key field to use (always 'name' for IMSP).
+     * @param mixed $ids     The ids of the contacts to load.
+     * @param string $owner  Only return contacts owned by this user.
+     * @param array $fields  List of fields to return.
      *
      * @return array  Hash containing the search results.
      */
-    function _read($criteria, $id, $fields)
+    function _read($key, $ids, $owner, $fields)
     {
         $results = array();
         if (!$this->_authenticated) {
             return $results;
         }
-        $id = array_values($id);
-        $idCount = count($id);
+        $ids = array_values($ids);
+        $idCount = count($ids);
         $members = array();
         $tmembers = array();
         $IMSPGroups = array();
 
         for ($i = 0; $i < $idCount; $i++) {
             $result = array();
-            if (!isset($IMSPGroups[$id[$i]])) {
-                $temp = $this->_imsp->getEntry($this->_bookName, $id[$i]);
+            if (!isset($IMSPGroups[$ids[$i]])) {
+                $temp = $this->_imsp->getEntry($this->_bookName, $ids[$i]);
             } else {
-                $temp = $IMSPGroups[$id[$i]];
+                $temp = $IMSPGroups[$ids[$i]];
             }
             if (is_a($temp, 'PEAR_Error')) {
                 continue;
@@ -184,15 +185,15 @@
                     if ($this->_noGroups) {
                         continue;
                     }
-                    if (!isset($IMSPGroups[$id[$i]])) {
-                        $IMSPGroups[$id[$i]] = $temp;
+                    if (!isset($IMSPGroups[$ids[$i]])) {
+                        $IMSPGroups[$ids[$i]] = $temp;
                     }
                     // move group ids to end of list
                     if ($idCount > count($IMSPGroups) &&
                         $idCount - count($IMSPGroups) > $i) {
-                        $id[] = $id[$i];
-                        unset($id[$i]);
-                        $id = array_values($id);
+                        $ids[] = $ids[$i];
+                        unset($ids[$i]);
+                        $ids = array_values($ids);
                         $i--;
                         continue;
                     }
@@ -289,7 +290,7 @@
             // generally require an existing conact entry in the current
             // address book for each group member (this is necessary for
             // those sources that may be used both in AND out of Horde).
-            $result = $this->_read('name', $members, array('email'));
+            $result = $this->_read('name', $members, null, array('email'));
             if (!is_a($result, 'PEAR_Error')) {
                 $count = count($result);
                 for ($i = 0; $i < $count; $i++) {
Index: lib/Driver/kolab.php
===================================================================
RCS file: /repository/turba/lib/Driver/kolab.php,v
retrieving revision 1.32
diff -u -r1.32 kolab.php
--- lib/Driver/kolab.php	2 Jan 2008 11:14:04 -0000	1.32
+++ lib/Driver/kolab.php	15 Feb 2008 05:56:34 -0000
@@ -75,17 +75,18 @@
 
     /**
      * Read the given data from the Kolab message store and returns the
-     * result's fields.
+     * results.
      *
-     * @param $criteria      Search criteria.
-     * @param $id            Data identifier.
-     * @param $fields        List of fields to return.
+     * @param string $key    The primary key field to use.
+     * @param mixed $ids     The ids of the contacts to load.
+     * @param string $owner  Only return contacts owned by this user.
+     * @param array $fields  List of fields to return.
      *
-     * @return               Hash containing the search results.
+     * @return array  Hash containing the search results.
      */
-    function _read($criteria, $id_list, $fields)
+    function _read($key, $ids, $owner, $fields)
     {
-        return $this->_wrapper->_read($criteria, $id_list, $fields);
+        return $this->_wrapper->_read($key, $ids, $fields);
     }
 
     /**
@@ -164,7 +165,7 @@
         if (isset($params['params']['default']) && $params['params']['default'] === true) {
             $share_id = Auth::getAuth();
         }
-        
+
         $result = &Turba::createShare($share_id, $params);
         return $result;
     }
Index: lib/Driver/ldap.php
===================================================================
RCS file: /repository/turba/lib/Driver/ldap.php,v
retrieving revision 1.87
diff -u -r1.87 ldap.php
--- lib/Driver/ldap.php	4 Jan 2007 05:08:59 -0000	1.87
+++ lib/Driver/ldap.php	15 Feb 2008 05:56:34 -0000
@@ -167,18 +167,19 @@
 
     /**
      * Reads the LDAP directory for a given element and returns
-     * the result's fields.
+     * the results.
      *
-     * @param string $criteria  Search criteria (must be 'dn').
-     * @param mixed  $dn        The dn of the object to read.
-     * @param array  $fields    List of fields to return.
+     * @param string $key    The primary key field to use.
+     * @param mixed $ids     The ids of the contacts to load.
+     * @param string $owner  Only return contacts owned by this user.
+     * @param array $fields  List of fields to return.
      *
      * @return array  Hash containing the search results.
      */
-    function _read($criteria, $dn, $fields)
+    function _read($key, $ids, $owner, $fields)
     {
         /* Only DN. */
-        if ($criteria != 'dn') {
+        if ($key != 'dn') {
             return array();
         }
 
@@ -192,9 +193,9 @@
         }
 
         /* Handle a request for multiple records. */
-        if (is_array($dn)) {
+        if (is_array($ids)) {
             $results = array();
-            foreach ($dn as $d) {
+            foreach ($ids as $d) {
                 $res = @ldap_read($this->_ds, String::convertCharset($d, NLS::getCharset(), $this->_params['charset']), $filter, $attr);
                 if ($res) {
                     if (!is_a($result = $this->_getResults($fields, $res), 'PEAR_Error')) {
@@ -209,7 +210,7 @@
             return $results;
         }
 
-        $res = @ldap_read($this->_ds, String::convertCharset($dn, NLS::getCharset(), $this->_params['charset']), $filter, $attr);
+        $res = @ldap_read($this->_ds, String::convertCharset($ids, NLS::getCharset(), $this->_params['charset']), $filter, $attr);
         if (!$res) {
             return PEAR::raiseError(sprintf(_("Read failed: (%s) %s"), ldap_errno($this->_ds), ldap_error($this->_ds)));
         }
Index: lib/Driver/share.php
===================================================================
RCS file: /repository/turba/lib/Driver/share.php,v
retrieving revision 1.13
diff -u -r1.13 share.php
--- lib/Driver/share.php	4 Jan 2008 18:53:28 -0000	1.13
+++ lib/Driver/share.php	15 Feb 2008 05:56:34 -0000
@@ -106,18 +106,19 @@
     }
 
     /**
-     * Reads the given data from the address book and returns the result's
-     * fields.
+     * Reads the given data from the address book and returns the
+     * results.
      *
-     * @param array $criteria  Search criteria.
-     * @param string $id       Data identifier.
-     * @param array $fields    List of fields to return.
+     * @param string $key    The primary key field to use.
+     * @param mixed $ids     The ids of the contacts to load.
+     * @param string $owner  Only return contacts owned by this user.
+     * @param array $fields  List of fields to return.
      *
-     * @return  Hash containing the search results.
+     * @return array  Hash containing the search results.
      */
-    function _read($criteria, $id, $fields)
+    function _read($key, $ids, $owner, $fields)
     {
-        return $this->_driver->_read($criteria, $id, $fields);
+        return $this->_driver->_read($key, $ids, $owner, $fields);
     }
 
     /**
Index: lib/Driver/sql.php
===================================================================
RCS file: /repository/turba/lib/Driver/sql.php,v
retrieving revision 1.112
diff -u -r1.112 sql.php
--- lib/Driver/sql.php	4 Jan 2008 18:53:28 -0000	1.112
+++ lib/Driver/sql.php	15 Feb 2008 05:56:34 -0000
@@ -194,33 +194,38 @@
     }
 
     /**
-     * Reads the given data from the SQL database and returns the result's
-     * fields.
+     * Reads the given data from the SQL database and returns the
+     * results.
      *
-     * @param array $criteria  Search criteria.
-     * @param string $id       Data identifier.
-     * @param array $fields    List of fields to return.
+     * @param string $key    The primary key field to use.
+     * @param mixed $ids     The ids of the contacts to load.
+     * @param string $owner  Only return contacts owned by this user.
+     * @param array $fields  List of fields to return.
      *
-     * @return  Hash containing the search results.
+     * @return array  Hash containing the search results.
      */
-    function _read($criteria, $id, $fields)
+    function _read($key, $ids, $owner, $fields)
     {
         $values = array();
 
         $in = '';
-        if (is_array($id)) {
-            if (!count($id)) {
+        if (is_array($ids)) {
+            if (!count($ids)) {
                 return array();
             }
 
-            foreach ($id as $key) {
+            foreach ($ids as $id) {
                 $in .= empty($in) ? '?' : ', ?';
-                $values[] = $this->_convertToDriver($key);
+                $values[] = $this->_convertToDriver($id);
             }
-            $where = $criteria . ' IN (' . $in . ')';
+            $where = $key . ' IN (' . $in . ')';
         } else {
-            $where = $criteria . ' = ?';
-            $values[] = $this->_convertToDriver($id);
+            $where = $key . ' = ?';
+            $values[] = $this->_convertToDriver($ids);
+        }
+        if (isset($this->map['__owner'])) {
+            $where .= ' AND ' . $this->map['__owner'] . ' = ?';
+            $values[] = $this->_convertToDriver($owner);
         }
         if (!empty($this->_params['filter'])) {
             $where .= ' AND ' . $this->_params['filter'];
Index: lib/Driver/vbook.php
===================================================================
RCS file: /repository/turba/lib/Driver/vbook.php,v
retrieving revision 1.10
diff -u -r1.10 vbook.php
--- lib/Driver/vbook.php	4 Jan 2008 18:53:28 -0000	1.10
+++ lib/Driver/vbook.php	15 Feb 2008 05:56:34 -0000
@@ -99,18 +99,18 @@
     }
 
     /**
-     * Reads the requested entries from the underlying source
+     * Reads the requested entries from the underlying source.
      *
-     * @param array $criteria  The field to check against.
-     * @param array $id        Array of data identifiers.
-     * @param array $fields    List of fields to return.
+     * @param string $key    The primary key field to use.
+     * @param mixed $ids     The ids of the contacts to load.
+     * @param string $owner  Only return contacts owned by this user.
+     * @param array $fields  List of fields to return.
      *
      * @return array  Hash containing the search results.
      */
-    function _read($criteria, $id, $fields)
+    function _read($key, $ids, $owner, $fields)
     {
-        $results = $this->_driver->_read($criteria, $id, $fields);
-        return $results;
+        return $this->_driver->_read($key, $ids, $owner, $fields);
     }
 
     /**
Index: lib/Object/Group.php
===================================================================
RCS file: /repository/turba/lib/Object/Group.php,v
retrieving revision 1.14
diff -u -r1.14 Group.php
--- lib/Object/Group.php	17 Jun 2007 18:21:18 -0000	1.14
+++ lib/Object/Group.php	15 Feb 2008 05:56:34 -0000
@@ -135,6 +135,23 @@
     }
 
     /**
+     * Count the number of contacts in this group.
+     *
+     * @return integer
+     *
+     * @since Turba 2.1.7
+     */
+    function count()
+    {
+        $children = @unserialize($this->attributes['__members']);
+        if (!is_array($children)) {
+            return 0;
+        } else {
+            return count($children);
+        }
+    }
+
+    /**
      * Retrieve the Objects in this group
      *
      * @param array $sort   The requested sort order which is passed to
@@ -171,14 +188,10 @@
                     $sourceId .= ':' . $owner;
                 }
                 $driver = &Turba_Driver::singleton($sourceId);
-                // Don't prune contacts if the source is not
-                // found, could just be temporarily unavailable.
                 if (!is_a($driver, 'PEAR_Error')) {
                     $contact = $driver->getObject($contactId);
                     if (is_a($contact, 'PEAR_Error')) {
-                        // Remove the contact if it no longer exists
-                        $this->removeMember($contactId, $sourceId);
-                        $modified = true;
+                        continue;
                     }
                 } else {
                     continue;
Index: lib/Views/Browse.php
===================================================================
RCS file: /repository/turba/lib/Views/Browse.php,v
retrieving revision 1.14
diff -u -r1.14 Browse.php
--- lib/Views/Browse.php	2 Jan 2008 11:14:04 -0000	1.14
+++ lib/Views/Browse.php	15 Feb 2008 05:56:34 -0000
@@ -343,6 +343,10 @@
                     if (!is_object($results = $list->listMembers($sortorder))) {
                         $notification->push(_("Failed to browse list"), 'horde.error');
                     } else {
+                        if ($results->count() != $list->count()) {
+                            $notification->push(sprintf(_("There are %d contact(s) in this list that are not viewable to you"),
+                                                        ($list->count() - $results->count())), 'horde.message');
+                        }
                         $view = &new Turba_ListView($results, null, $columns);
                         $view->setType('list');
                     }
Index: docs/CHANGES
===================================================================
RCS file: /repository/turba/docs/CHANGES,v
retrieving revision 1.181.2.130
diff -u -r1.181.2.130 CHANGES
--- docs/CHANGES	10 Feb 2008 16:11:37 -0000	1.181.2.130
+++ docs/CHANGES	15 Feb 2008 05:56:45 -0000
@@ -2,6 +2,8 @@
 v2.2-cvs
 --------
 
+[cjh] SECURITY: Fix unchecked access to contacts in the same SQL table
+      (Bug #6208).
 [jan] Add configuration to more flexibly parse full names into the name parts.
 
 
Index: docs/RELEASE_NOTES
===================================================================
RCS file: /repository/turba/docs/RELEASE_NOTES,v
retrieving revision 1.22.2.30
diff -u -r1.22.2.30 RELEASE_NOTES
--- docs/RELEASE_NOTES	20 Jan 2008 16:27:41 -0000	1.22.2.30
+++ docs/RELEASE_NOTES	15 Feb 2008 05:56:45 -0000
@@ -12,11 +12,11 @@
  * 8 - Minor security fixes
  * 9 - Major security fixes
  */
-$this->notes['fm']['focus'] = 4;
+$this->notes['fm']['focus'] = 8;
 
 /* Mailing list release notes. */
 $this->notes['ml']['changes'] = <<<ML
-The Horde Team is pleased to announce the second release candidate of the Turba
+The Horde Team is pleased to announce the third release candidate of the Turba
 Contact Manager version H3 (2.2).
 
 Turba is the Horde contact management application. It is a production level
@@ -28,20 +28,13 @@
 Testing is requested and comments are encouraged.
 Updated translations would also be great.
 
-The major changes compared to the Turba version H3 (2.2-RC1) are:
-    * Fixed privilege escalation in the Horde API.
-    * Fixed several issues with composite fields.
-    * Improved address book management.
-    * Improved listTimeObjects API.
-    * Further bugfixes and improvements.
+The major changes compared to the Turba version H3 (2.2-RC2) are:
+    * Fixed unchecked access to contacts in the same SQL table (Bug #6208).
 ML;
 
 /* Freshmeat release notes, not more than 600 characters. */
 $this->notes['fm']['changes'] = <<<FM
-A privilege escalation in the Horde API has been fixed.
-Several issues with composite fields have been fixed.
-The address book management and the listTimeObjects API have been improved.
-Further bugfixes and improvements have been made.
+Fix unchecked access to contacts in the same SQL table.
 FM;
 
 $this->notes['name'] = 'Turba';
Index: lib/Driver.php
===================================================================
RCS file: /repository/turba/lib/Driver.php,v
retrieving revision 1.57.2.43
diff -u -r1.57.2.43 Driver.php
--- lib/Driver.php	11 Feb 2008 00:47:39 -0000	1.57.2.43
+++ lib/Driver.php	15 Feb 2008 05:56:45 -0000
@@ -599,9 +599,8 @@
      */
     function &getObjects($objectIds)
     {
-        $criteria = $this->map['__key'];
-
-        $objects = $this->_read($criteria, $objectIds,
+        $objects = $this->_read($this->map['__key'], $objectIds,
+                                $this->getContactOwner(),
                                 array_values($this->fields));
         if (is_a($objects, 'PEAR_Error')) {
             return $objects;
@@ -1554,22 +1553,22 @@
     }
 
     /**
-     * Reads the given data from the address book and returns the result's
-     * fields.
+     * Reads the given data from the address book and returns the results.
      *
-     * @param array $criteria  Search criteria.
-     * @param string $id       Data identifier.
-     * @param array $fields    List of fields to return.
+     * @param string $key    The primary key field to use.
+     * @param mixed $ids     The ids of the contacts to load.
+     * @param string $owner  Only return contacts owned by this user.
+     * @param array $fields  List of fields to return.
      *
-     * @return  Hash containing the search results.
+     * @return array  Hash containing the search results.
      */
-    function _read($criteria, $id, $fields)
+    function _read($key, $ids, $owner, $fields)
     {
         return PEAR::raiseError(_("Reading contacts is not available."));
     }
 
     /**
-     * Adds the specified object to the SQL database.
+     * Adds the specified contact to the SQL database.
      */
     function _add($attributes)
     {
@@ -1577,7 +1576,7 @@
     }
 
     /**
-     * Deletes the specified object from the SQL database.
+     * Deletes the specified contact from the SQL database.
      */
     function _delete($object_key, $object_id)
     {
Index: lib/Driver/imsp.php
===================================================================
RCS file: /repository/turba/lib/Driver/imsp.php,v
retrieving revision 1.21.4.19
diff -u -r1.21.4.19 imsp.php
--- lib/Driver/imsp.php	4 Jan 2008 19:13:21 -0000	1.21.4.19
+++ lib/Driver/imsp.php	15 Feb 2008 05:56:45 -0000
@@ -131,7 +131,7 @@
         }
 
         /* Now we have a list of names, get the rest. */
-        $result = $this->_read('name', $names, $fields);
+        $result = $this->_read('name', $names, null, $fields);
         if (is_array($result)) {
             $results = $result;
         }
@@ -143,32 +143,33 @@
 
     /**
      * Reads the given data from the IMSP server and returns the
-     * result's fields.
+     * results.
      *
-     * @param array $criteria  (Ignored: Always 'name' for IMSP) Search criteria.
-     * @param array $id        Array of data identifiers.
-     * @param array $fields    List of fields to return.
+     * @param string $key    The primary key field to use (always 'name' for IMSP).
+     * @param mixed $ids     The ids of the contacts to load.
+     * @param string $owner  Only return contacts owned by this user.
+     * @param array $fields  List of fields to return.
      *
      * @return array  Hash containing the search results.
      */
-    function _read($criteria, $id, $fields)
+    function _read($key, $ids, $owner, $fields)
     {
         $results = array();
         if (!$this->_authenticated) {
             return $results;
         }
-        $id = array_values($id);
-        $idCount = count($id);
+        $ids = array_values($ids);
+        $idCount = count($ids);
         $members = array();
         $tmembers = array();
         $IMSPGroups = array();
 
         for ($i = 0; $i < $idCount; $i++) {
             $result = array();
-            if (!isset($IMSPGroups[$id[$i]])) {
-                $temp = $this->_imsp->getEntry($this->_bookName, $id[$i]);
+            if (!isset($IMSPGroups[$ids[$i]])) {
+                $temp = $this->_imsp->getEntry($this->_bookName, $ids[$i]);
             } else {
-                $temp = $IMSPGroups[$id[$i]];
+                $temp = $IMSPGroups[$ids[$i]];
             }
             if (is_a($temp, 'PEAR_Error')) {
                 continue;
@@ -184,15 +185,15 @@
                     if ($this->_noGroups) {
                         continue;
                     }
-                    if (!isset($IMSPGroups[$id[$i]])) {
-                        $IMSPGroups[$id[$i]] = $temp;
+                    if (!isset($IMSPGroups[$ids[$i]])) {
+                        $IMSPGroups[$ids[$i]] = $temp;
                     }
                     // move group ids to end of list
                     if ($idCount > count($IMSPGroups) &&
                         $idCount - count($IMSPGroups) > $i) {
-                        $id[] = $id[$i];
-                        unset($id[$i]);
-                        $id = array_values($id);
+                        $ids[] = $ids[$i];
+                        unset($ids[$i]);
+                        $ids = array_values($ids);
                         $i--;
                         continue;
                     }
@@ -289,7 +290,7 @@
             // generally require an existing conact entry in the current
             // address book for each group member (this is necessary for
             // those sources that may be used both in AND out of Horde).
-            $result = $this->_read('name', $members, array('email'));
+            $result = $this->_read('name', $members, null, array('email'));
             if (!is_a($result, 'PEAR_Error')) {
                 $count = count($result);
                 for ($i = 0; $i < $count; $i++) {
Index: lib/Driver/kolab.php
===================================================================
RCS file: /repository/turba/lib/Driver/kolab.php,v
retrieving revision 1.5.10.11
diff -u -r1.5.10.11 kolab.php
--- lib/Driver/kolab.php	2 Jan 2008 11:32:40 -0000	1.5.10.11
+++ lib/Driver/kolab.php	15 Feb 2008 05:56:46 -0000
@@ -75,17 +75,18 @@
 
     /**
      * Read the given data from the Kolab message store and returns the
-     * result's fields.
+     * results.
      *
-     * @param $criteria      Search criteria.
-     * @param $id            Data identifier.
-     * @param $fields        List of fields to return.
+     * @param string $key    The primary key field to use.
+     * @param mixed $ids     The ids of the contacts to load.
+     * @param string $owner  Only return contacts owned by this user.
+     * @param array $fields  List of fields to return.
      *
-     * @return               Hash containing the search results.
+     * @return array  Hash containing the search results.
      */
-    function _read($criteria, $id_list, $fields)
+    function _read($key, $ids, $owner, $fields)
     {
-        return $this->_wrapper->_read($criteria, $id_list, $fields);
+        return $this->_wrapper->_read($key, $ids, $fields);
     }
 
     /**
@@ -164,7 +165,7 @@
         if (isset($params['params']['default']) && $params['params']['default'] === true) {
             $share_id = Auth::getAuth();
         }
-        
+
         $result = &Turba::createShare($share_id, $params);
         return $result;
     }
Index: lib/Driver/ldap.php
===================================================================
RCS file: /repository/turba/lib/Driver/ldap.php,v
retrieving revision 1.54.4.17
diff -u -r1.54.4.17 ldap.php
--- lib/Driver/ldap.php	20 Dec 2007 14:34:30 -0000	1.54.4.17
+++ lib/Driver/ldap.php	15 Feb 2008 05:56:46 -0000
@@ -167,18 +167,19 @@
 
     /**
      * Reads the LDAP directory for a given element and returns
-     * the result's fields.
+     * the results.
      *
-     * @param string $criteria  Search criteria (must be 'dn').
-     * @param mixed  $dn        The dn of the object to read.
-     * @param array  $fields    List of fields to return.
+     * @param string $key    The primary key field to use.
+     * @param mixed $ids     The ids of the contacts to load.
+     * @param string $owner  Only return contacts owned by this user.
+     * @param array $fields  List of fields to return.
      *
      * @return array  Hash containing the search results.
      */
-    function _read($criteria, $dn, $fields)
+    function _read($key, $ids, $owner, $fields)
     {
         /* Only DN. */
-        if ($criteria != 'dn') {
+        if ($key != 'dn') {
             return array();
         }
 
@@ -192,9 +193,9 @@
         }
 
         /* Handle a request for multiple records. */
-        if (is_array($dn)) {
+        if (is_array($ids)) {
             $results = array();
-            foreach ($dn as $d) {
+            foreach ($ids as $d) {
                 $res = @ldap_read($this->_ds, String::convertCharset($d, NLS::getCharset(), $this->_params['charset']), $filter, $attr);
                 if ($res) {
                     if (!is_a($result = $this->_getResults($fields, $res), 'PEAR_Error')) {
@@ -209,7 +210,7 @@
             return $results;
         }
 
-        $res = @ldap_read($this->_ds, String::convertCharset($dn, NLS::getCharset(), $this->_params['charset']), $filter, $attr);
+        $res = @ldap_read($this->_ds, String::convertCharset($ids, NLS::getCharset(), $this->_params['charset']), $filter, $attr);
         if (!$res) {
             return PEAR::raiseError(sprintf(_("Read failed: (%s) %s"), ldap_errno($this->_ds), ldap_error($this->_ds)));
         }
Index: lib/Driver/share.php
===================================================================
RCS file: /repository/turba/lib/Driver/share.php,v
retrieving revision 1.11.2.3
diff -u -r1.11.2.3 share.php
--- lib/Driver/share.php	4 Jan 2008 19:13:21 -0000	1.11.2.3
+++ lib/Driver/share.php	15 Feb 2008 05:56:46 -0000
@@ -106,18 +106,19 @@
     }
 
     /**
-     * Reads the given data from the address book and returns the result's
-     * fields.
+     * Reads the given data from the address book and returns the
+     * results.
      *
-     * @param array $criteria  Search criteria.
-     * @param string $id       Data identifier.
-     * @param array $fields    List of fields to return.
+     * @param string $key    The primary key field to use.
+     * @param mixed $ids     The ids of the contacts to load.
+     * @param string $owner  Only return contacts owned by this user.
+     * @param array $fields  List of fields to return.
      *
-     * @return  Hash containing the search results.
+     * @return array  Hash containing the search results.
      */
-    function _read($criteria, $id, $fields)
+    function _read($key, $ids, $owner, $fields)
     {
-        return $this->_driver->_read($criteria, $id, $fields);
+        return $this->_driver->_read($key, $ids, $owner, $fields);
     }
 
     /**
Index: lib/Driver/sql.php
===================================================================
RCS file: /repository/turba/lib/Driver/sql.php,v
retrieving revision 1.59.10.21
diff -u -r1.59.10.21 sql.php
--- lib/Driver/sql.php	4 Jan 2008 19:13:21 -0000	1.59.10.21
+++ lib/Driver/sql.php	15 Feb 2008 05:56:46 -0000
@@ -194,33 +194,38 @@
     }
 
     /**
-     * Reads the given data from the SQL database and returns the result's
-     * fields.
+     * Reads the given data from the SQL database and returns the
+     * results.
      *
-     * @param array $criteria  Search criteria.
-     * @param string $id       Data identifier.
-     * @param array $fields    List of fields to return.
+     * @param string $key    The primary key field to use.
+     * @param mixed $ids     The ids of the contacts to load.
+     * @param string $owner  Only return contacts owned by this user.
+     * @param array $fields  List of fields to return.
      *
-     * @return  Hash containing the search results.
+     * @return array  Hash containing the search results.
      */
-    function _read($criteria, $id, $fields)
+    function _read($key, $ids, $owner, $fields)
     {
         $values = array();
 
         $in = '';
-        if (is_array($id)) {
-            if (!count($id)) {
+        if (is_array($ids)) {
+            if (!count($ids)) {
                 return array();
             }
 
-            foreach ($id as $key) {
+            foreach ($ids as $id) {
                 $in .= empty($in) ? '?' : ', ?';
-                $values[] = $this->_convertToDriver($key);
+                $values[] = $this->_convertToDriver($id);
             }
-            $where = $criteria . ' IN (' . $in . ')';
+            $where = $key . ' IN (' . $in . ')';
         } else {
-            $where = $criteria . ' = ?';
-            $values[] = $this->_convertToDriver($id);
+            $where = $key . ' = ?';
+            $values[] = $this->_convertToDriver($ids);
+        }
+        if (isset($this->map['__owner'])) {
+            $where .= ' AND ' . $this->map['__owner'] . ' = ?';
+            $values[] = $this->_convertToDriver($owner);
         }
         if (!empty($this->_params['filter'])) {
             $where .= ' AND ' . $this->_params['filter'];
Index: lib/Driver/vbook.php
===================================================================
RCS file: /repository/turba/lib/Driver/vbook.php,v
retrieving revision 1.8.2.3
diff -u -r1.8.2.3 vbook.php
--- lib/Driver/vbook.php	4 Jan 2008 19:13:21 -0000	1.8.2.3
+++ lib/Driver/vbook.php	15 Feb 2008 05:56:46 -0000
@@ -99,18 +99,18 @@
     }
 
     /**
-     * Reads the requested entries from the underlying source
+     * Reads the requested entries from the underlying source.
      *
-     * @param array $criteria  The field to check against.
-     * @param array $id        Array of data identifiers.
-     * @param array $fields    List of fields to return.
+     * @param string $key    The primary key field to use.
+     * @param mixed $ids     The ids of the contacts to load.
+     * @param string $owner  Only return contacts owned by this user.
+     * @param array $fields  List of fields to return.
      *
      * @return array  Hash containing the search results.
      */
-    function _read($criteria, $id, $fields)
+    function _read($key, $ids, $owner, $fields)
     {
-        $results = $this->_driver->_read($criteria, $id, $fields);
-        return $results;
+        return $this->_driver->_read($key, $ids, $owner, $fields);
     }
 
     /**
Index: lib/Object/Group.php
===================================================================
RCS file: /repository/turba/lib/Object/Group.php,v
retrieving revision 1.9.2.3
diff -u -r1.9.2.3 Group.php
--- lib/Object/Group.php	20 Dec 2007 14:34:31 -0000	1.9.2.3
+++ lib/Object/Group.php	15 Feb 2008 05:56:46 -0000
@@ -135,6 +135,23 @@
     }
 
     /**
+     * Count the number of contacts in this group.
+     *
+     * @return integer
+     *
+     * @since Turba 2.1.7
+     */
+    function count()
+    {
+        $children = @unserialize($this->attributes['__members']);
+        if (!is_array($children)) {
+            return 0;
+        } else {
+            return count($children);
+        }
+    }
+
+    /**
      * Retrieve the Objects in this group
      *
      * @param array $sort   The requested sort order which is passed to
@@ -171,14 +188,10 @@
                     $sourceId .= ':' . $owner;
                 }
                 $driver = &Turba_Driver::singleton($sourceId);
-                // Don't prune contacts if the source is not
-                // found, could just be temporarily unavailable.
                 if (!is_a($driver, 'PEAR_Error')) {
                     $contact = $driver->getObject($contactId);
                     if (is_a($contact, 'PEAR_Error')) {
-                        // Remove the contact if it no longer exists
-                        $this->removeMember($contactId, $sourceId);
-                        $modified = true;
+                        continue;
                     }
                 } else {
                     continue;
Index: lib/Views/Browse.php
===================================================================
RCS file: /repository/turba/lib/Views/Browse.php,v
retrieving revision 1.12.2.3
diff -u -r1.12.2.3 Browse.php
--- lib/Views/Browse.php	2 Jan 2008 11:32:40 -0000	1.12.2.3
+++ lib/Views/Browse.php	15 Feb 2008 05:56:46 -0000
@@ -343,6 +343,10 @@
                     if (!is_object($results = $list->listMembers($sortorder))) {
                         $notification->push(_("Failed to browse list"), 'horde.error');
                     } else {
+                        if ($results->count() != $list->count()) {
+                            $notification->push(sprintf(_("There are %d contact(s) in this list that are not viewable to you"),
+                                                        ($list->count() - $results->count())), 'horde.message');
+                        }
                         $view = &new Turba_ListView($results, null, $columns);
                         $view->setType('list');
                     }
Index: browse.php
===================================================================
RCS file: /repository/turba/browse.php,v
retrieving revision 1.76.2.25
diff -u -r1.76.2.25 browse.php
--- browse.php	3 Aug 2007 23:54:05 -0000	1.76.2.25
+++ browse.php	15 Feb 2008 05:56:26 -0000
@@ -315,6 +315,10 @@
             if (!is_object($results = $list->listMembers($sortcolumn, $prefs->getValue('sortdir')))) {
                 $notification->push(_("Failed to browse list"), 'horde.error');
             } else {
+                if ($results->count() != $list->count()) {
+                    $notification->push(sprintf(_("There are %d contact(s) in this list that are not viewable to you"),
+                                                ($list->count() - $results->count())), 'horde.message');
+                }
                 $view = &new Turba_ListView($results);
                 $view->setType('list');
             }
Index: docs/CHANGES
===================================================================
RCS file: /repository/turba/docs/CHANGES,v
retrieving revision 1.181.2.114.2.3
diff -u -r1.181.2.114.2.3 CHANGES
--- docs/CHANGES	9 Jan 2008 22:41:10 -0000	1.181.2.114.2.3
+++ docs/CHANGES	15 Feb 2008 05:56:26 -0000
@@ -2,7 +2,8 @@
 v2.1.7-cvs
 ----------
 
-
+[cjh] SECURITY: Fix unchecked access to contacts in the same SQL table
+      (Bug #6208).
 
 
 ------
Index: docs/RELEASE_NOTES
===================================================================
RCS file: /repository/turba/docs/RELEASE_NOTES,v
retrieving revision 1.22.2.28.2.2
diff -u -r1.22.2.28.2.2 RELEASE_NOTES
--- docs/RELEASE_NOTES	9 Jan 2008 13:33:07 -0000	1.22.2.28.2.2
+++ docs/RELEASE_NOTES	15 Feb 2008 05:56:26 -0000
@@ -17,25 +17,24 @@
 /* Mailing list release notes. */
 $this->notes['ml']['changes'] = <<<ML
 The Horde Team is pleased to announce the final release of the Turba Contact
-Manager version H3 (2.1.6).
+Manager version H3 (2.1.7).
 
-This is a security release that fixes a privilege escalation in the Horde
-API. All users are encouraged to upgrade to this version.
+This is a security release that fixes unchecked access to contacts in
+the same SQL table, if the unique key of another user's contact can be
+guessed.  All users are encouraged to upgrade to this version.
 
 Turba is the Horde contact management application. It is a production level
 address book, and makes heavy use of the Horde framework to provide
 integration with IMP and other Horde applications. It supports SQL, LDAP,
 Kolab, and IMSP address books.
 
-Major changes compared to the Turba H3 (2.1.5) version are:
-    * Fixed privilege escalation in the Horde API.
-    * Updated Japanese translation.
+Major changes compared to the Turba H3 (2.1.6) version are:
+    * Fixed unchecked access to contacts in the same SQL table (Bug #6208).
 ML;
 
 /* Freshmeat release notes, not more than 600 characters. */
 $this->notes['fm']['changes'] = <<<FM
-A privilege escalation in the Horde API has been fixed.
-The Japanese translation has been updated.
+Fix unchecked access to contacts in the same SQL table.
 FM;
 
 $this->notes['name'] = 'Turba';
Index: lib/Driver/sql.php
===================================================================
RCS file: /repository/turba/lib/Driver/sql.php,v
retrieving revision 1.59.10.17
diff -u -r1.59.10.17 sql.php
--- lib/Driver/sql.php	30 Nov 2006 21:33:47 -0000	1.59.10.17
+++ lib/Driver/sql.php	15 Feb 2008 05:56:26 -0000
@@ -182,6 +182,15 @@
             $where = $criteria . ' = ?';
             $values[] = $this->_convertToDriver($id);
         }
+        if (isset($this->map['__owner'])) {
+            if ($this->usingShares) {
+                $owner = $this->share->get('uid');
+            } else {
+                $owner = Auth::getAuth();
+            }
+            $where .= ' AND ' . $this->map['__owner'] . ' = ?';
+            $values[] = $this->_convertToDriver($owner);
+        }
         if (!empty($this->_params['filter'])) {
             $where .= ' AND ' . $this->_params['filter'];
         }
Index: lib/Object/Group.php
===================================================================
RCS file: /repository/turba/lib/Object/Group.php,v
retrieving revision 1.9.2.2
diff -u -r1.9.2.2 Group.php
--- lib/Object/Group.php	14 Nov 2005 21:03:27 -0000	1.9.2.2
+++ lib/Object/Group.php	15 Feb 2008 05:56:26 -0000
@@ -124,6 +124,23 @@
     }
 
     /**
+     * Count the number of contacts in this group.
+     *
+     * @return integer
+     *
+     * @since Turba 2.1.7
+     */
+    function count()
+    {
+        $children = @unserialize($this->attributes['__members']);
+        if (!is_array($children)) {
+            return 0;
+        } else {
+            return count($children);
+        }
+    }
+
+    /**
      * Retrieve the Objects in this group
      *
      * @param $sort_criteria     The requested sort order which is passed to
@@ -163,14 +180,10 @@
                     $sourceId .= ':' . $owner;
                 }
                 $driver = &Turba_Driver::singleton($sourceId);
-                // Don't prune contacts if the source is not
-                // found, could just be temporarily unavailable.
                 if (!is_a($driver, 'PEAR_Error')) {
                     $contact = $driver->getObject($contactId);
                     if (is_a($contact, 'PEAR_Error')) {
-                        // Remove the contact if it no longer exists
-                        $this->removeMember($contactId, $sourceId);
-                        $modified = true;
+                        continue;
                     }
                 } else {
                     continue;

Reply via email to