CVE-2023-34465: Improper Privilege Management (CWE-269)
value: XWiki Platform is a generic wiki platform. Starting in version 11.8-rc-1 and prior to versions 14.4.8, 14.10.6, and 15.2, `Mail.MailConfig` can be edited by any logged-in user by default. Consequently, they can change the mail obfuscation configuration and view and edit the mail sending configuration, including the smtp domain name and credentials. The problem has been patched in XWiki 14.4.8, 14.10.6, and 15.1. As a workaround, the rights of the `Mail.MailConfig` page can be manually updated so that only a set of trusted users can view, edit and delete it (e.g., the `XWiki.XWikiAdminGroup` group).
CWE description: The product does not properly assign, modify, track, or check privileges for an actor, creating an unintended sphere of control for that actor.
CVSS v3 base score: 8.1
Published: 2023-06-23T16:15Z
Repository: https://github.com/xwiki/xwiki-platform
Language: Java
Files changed: xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/internal/DocumentInitializerRightsManager.java, xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/test/java/org/xwiki/security/internal/DocumentInitializerRightsManagerTest.java
Commit date: 2023-02-23 11:11:04 +0100
Commit message: XWIKI-20671: Fix Mail.MailConfig initialization

Diff: diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/internal/DocumentInitializerRightsManager.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/internal/DocumentInitializerRightsManager.java
index 2e6e21aef4041..fd75568da8a2c 100644
--- a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/internal/DocumentInitializerRightsManager.java
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/internal/DocumentInitializerRightsManager.java
@@ -18,26 +18,27 @@
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  */
 package org.xwiki.security.internal;
 
 import java.util.List;
-import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 import javax.inject.Provider;
 import javax.inject.Singleton;
 
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.xwiki.component.annotation.Component;
 import org.xwiki.security.authorization.Right;
 
 import com.xpn.xwiki.XWikiContext;
 import com.xpn.xwiki.XWikiException;
 import com.xpn.xwiki.doc.MandatoryDocumentInitializer;
 import com.xpn.xwiki.doc.XWikiDocument;
 import com.xpn.xwiki.objects.BaseObject;
 
+import static java.util.stream.Collectors.joining;
 import static org.xwiki.security.authorization.Right.DELETE;
 import static org.xwiki.security.authorization.Right.EDIT;
 import static org.xwiki.security.authorization.Right.VIEW;
 import static org.xwiki.security.internal.XWikiConstants.ALLOW_FIELD_NAME;
 import static org.xwiki.security.internal.XWikiConstants.GROUPS_FIELD_NAME;
@@ -74,36 +75,65 @@
      *     been modified because of an error during the modification)
      */
     public boolean restrictToAdmin(XWikiDocument document)
     {
         boolean updated = false;
+        List<Right> rights = List.of(VIEW, EDIT, DELETE);
+
+        if (fixBadlyInitializedRights(document, rights)) {
+            updated = true;
+        }
+
         // If some rights have already been set on the document, we consider that it has already been protected 
         // manually.
         if (document.getXObjects(LOCAL_CLASS_REFERENCE).isEmpty()) {
-            updated = initializeRights(document, XWIKI_ADMIN_GROUP_DOCUMENT_REFERENCE, List.of(VIEW, EDIT, DELETE));
+            updated = updated || initializeRights(document, rights);
         }
 
         return updated;
     }
 
-    private boolean initializeRights(XWikiDocument document, String xwikiAdminGroupDocumentReference,
-        List<Right> rights)
+    private boolean initializeRights(XWikiDocument document, List<Right> rights)
     {
         boolean updated = false;
 
         try {
-            XWikiContext xwikiContext = this.xcontextProvider.get();
-            BaseObject object = document.newXObject(LOCAL_CLASS_REFERENCE, xwikiContext);
-            XWikiContext xWikiContext = this.xcontextProvider.get();
-            object.set(GROUPS_FIELD_NAME, xwikiAdminGroupDocumentReference, xWikiContext);
-            object.set(LEVELS_FIELD_NAME, rights.stream().map(Right::getName).collect(Collectors.toList()),
-                xWikiContext);
-            object.set(ALLOW_FIELD_NAME, 1, xWikiContext);
+            BaseObject object = document.newXObject(LOCAL_CLASS_REFERENCE, this.xcontextProvider.get());
+            setRights(object, rights);
             updated = true;
         } catch (XWikiException e) {
             this.logger.error(String.format("Error adding a [%s] object to the document [%s]", LOCAL_CLASS_REFERENCE,
                 document.getDocumentReference()));
         }
 
         return updated;
     }
+
+    /**
+     * Because of XWIKI-20519, the levels of the rights of {@code XWiki.XWikiAdminGroup} can be badly initialized, and
+     * needs to be fixed. This is the case when upgrading from instances running exactly version 14.10.5 or 15.1-rc-1.
+     *
+     * @param document the document to fix
+     * @param rights the rights to set to the {@code XWiki.XWikiAdminGroup} group
+     */
+    private boolean fixBadlyInitializedRights(XWikiDocument document, List<Right> rights)
+    {
+        boolean updated = false;
+        if (document.getXObjects(LOCAL_CLASS_REFERENCE).size() == 1) {
+            BaseObject object = document.getXObject(LOCAL_CLASS_REFERENCE);
+            if (StringUtils.isEmpty(object.getStringValue(LEVELS_FIELD_NAME))
+                && StringUtils.isEmpty(object.getLargeStringValue(GROUPS_FIELD_NAME)))
+            {
+                setRights(object, rights);
+                updated = true;
+            }
+        }
+        return updated;
+    }
+
+    private void setRights(BaseObject object, List<Right> rights)
+    {
+        object.setLargeStringValue(GROUPS_FIELD_NAME, XWIKI_ADMIN_GROUP_DOCUMENT_REFERENCE);
+        object.setStringValue(LEVELS_FIELD_NAME, rights.stream().map(Right::getName).collect(joining(",")));
+        object.setIntValue(ALLOW_FIELD_NAME, 1);
+    }
 }
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/test/java/org/xwiki/security/internal/DocumentInitializerRightsManagerTest.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/test/java/org/xwiki/security/internal/DocumentInitializerRightsManagerTest.java
index 108b819e647c7..cad7f78eb02c1 100644
--- a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/test/java/org/xwiki/security/internal/DocumentInitializerRightsManagerTest.java
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/test/java/org/xwiki/security/internal/DocumentInitializerRightsManagerTest.java
@@ -90,22 +90,37 @@ void setUp(MockitoComponentManager componentManager) throws Exception
     }
 
     @Test
     void restrictToAdminSkipWhenAlreadyHasRights() throws Exception
     {
-        this.document.newXObject(LOCAL_CLASS_REFERENCE, this.xWikiContext);
+        BaseObject baseObject = this.document.newXObject(LOCAL_CLASS_REFERENCE, this.xWikiContext);
+        baseObject.setLargeStringValue(LEVELS_FIELD_NAME, "edit");
         assertFalse(this.rightsManager.restrictToAdmin(this.document));
     }
 
+    @Test
+    void restrictToAdminBadlyInitialized() throws Exception
+    {
+        BaseObject object = this.document.newXObject(LOCAL_CLASS_REFERENCE, this.xWikiContext);
+        object.setLargeStringValue(GROUPS_FIELD_NAME, "");
+        object.setLargeStringValue(LEVELS_FIELD_NAME, "");
+        object.setIntValue(ALLOW_FIELD_NAME, 1);
+        assertTrue(this.rightsManager.restrictToAdmin(this.document));
+        BaseObject xObject = this.document.getXObject(LOCAL_CLASS_REFERENCE);
+        assertEquals("XWiki.XWikiAdminGroup", xObject.getStringValue(GROUPS_FIELD_NAME));
+        assertEquals("view,edit,delete", xObject.getStringValue(LEVELS_FIELD_NAME));
+        assertEquals("1", xObject.getStringValue(ALLOW_FIELD_NAME));
+    }
+
     @Test
     void restrictToAdmin()
     {
         assertTrue(this.rightsManager.restrictToAdmin(this.document));
         assertEquals(1, this.document.getXObjects(LOCAL_CLASS_REFERENCE).size());
         BaseObject xObject = this.document.getXObject(LOCAL_CLASS_REFERENCE);
         assertEquals("XWiki.XWikiAdminGroup", xObject.getStringValue(GROUPS_FIELD_NAME));
-        assertEquals("view, edit, delete", xObject.getStringValue(LEVELS_FIELD_NAME));
+        assertEquals("view,edit,delete", xObject.getStringValue(LEVELS_FIELD_NAME));
         assertEquals("1", xObject.getStringValue(ALLOW_FIELD_NAME));
     }
 
     @Test
     void restrictToAdminWithException() throws XWikiException

Vulnerable code: import java.util.stream.Collectors;
            updated = initializeRights(document, XWIKI_ADMIN_GROUP_DOCUMENT_REFERENCE, List.of(VIEW, EDIT, DELETE));
    private boolean initializeRights(XWikiDocument document, String xwikiAdminGroupDocumentReference,
        List<Right> rights)
            XWikiContext xwikiContext = this.xcontextProvider.get();
            BaseObject object = document.newXObject(LOCAL_CLASS_REFERENCE, xwikiContext);
            XWikiContext xWikiContext = this.xcontextProvider.get();
            object.set(GROUPS_FIELD_NAME, xwikiAdminGroupDocumentReference, xWikiContext);
            object.set(LEVELS_FIELD_NAME, rights.stream().map(Right::getName).collect(Collectors.toList()),
                xWikiContext);
            object.set(ALLOW_FIELD_NAME, 1, xWikiContext);
        this.document.newXObject(LOCAL_CLASS_REFERENCE, this.xWikiContext);
        assertEquals("view, edit, delete", xObject.getStringValue(LEVELS_FIELD_NAME));
Fixed code: import org.apache.commons.lang3.StringUtils;
import static java.util.stream.Collectors.joining;
        List<Right> rights = List.of(VIEW, EDIT, DELETE);

        if (fixBadlyInitializedRights(document, rights)) {
            updated = true;
        }

            updated = updated || initializeRights(document, rights);
    private boolean initializeRights(XWikiDocument document, List<Right> rights)
            BaseObject object = document.newXObject(LOCAL_CLASS_REFERENCE, this.xcontextProvider.get());
            setRights(object, rights);

    /**
     * Because of XWIKI-20519, the levels of the rights of {@code XWiki.XWikiAdminGroup} can be badly initialized, and
     * needs to be fixed. This is the case when upgrading from instances running exactly version 14.10.5 or 15.1-rc-1.
     *
     * @param document the document to fix
     * @param rights the rights to set to the {@code XWiki.XWikiAdminGroup} group
     */
    private boolean fixBadlyInitializedRights(XWikiDocument document, List<Right> rights)
    {
        boolean updated = false;
        if (document.getXObjects(LOCAL_CLASS_REFERENCE).size() == 1) {
            BaseObject object = document.getXObject(LOCAL_CLASS_REFERENCE);
            if (StringUtils.isEmpty(object.getStringValue(LEVELS_FIELD_NAME))
                && StringUtils.isEmpty(object.getLargeStringValue(GROUPS_FIELD_NAME)))
            {
                setRights(object, rights);
                updated = true;
            }
        }
        return updated;
    }

    private void setRights(BaseObject object, List<Right> rights)
    {
        object.setLargeStringValue(GROUPS_FIELD_NAME, XWIKI_ADMIN_GROUP_DOCUMENT_REFERENCE);
        object.setStringValue(LEVELS_FIELD_NAME, rights.stream().map(Right::getName).collect(joining(",")));
        object.setIntValue(ALLOW_FIELD_NAME, 1);
    }
        BaseObject baseObject = this.document.newXObject(LOCAL_CLASS_REFERENCE, this.xWikiContext);
        baseObject.setLargeStringValue(LEVELS_FIELD_NAME, "edit");
    @Test
    void restrictToAdminBadlyInitialized() throws Exception
    {
        BaseObject object = this.document.newXObject(LOCAL_CLASS_REFERENCE, this.xWikiContext);
        object.setLargeStringValue(GROUPS_FIELD_NAME, "");
        object.setLargeStringValue(LEVELS_FIELD_NAME, "");
        object.setIntValue(ALLOW_FIELD_NAME, 1);
        assertTrue(this.rightsManager.restrictToAdmin(this.document));
        BaseObject xObject = this.document.getXObject(LOCAL_CLASS_REFERENCE);
        assertEquals("XWiki.XWikiAdminGroup", xObject.getStringValue(GROUPS_FIELD_NAME));
        assertEquals("view,edit,delete", xObject.getStringValue(LEVELS_FIELD_NAME));
        assertEquals("1", xObject.getStringValue(ALLOW_FIELD_NAME));
    }

        assertEquals("view,edit,delete", xObject.getStringValue(LEVELS_FIELD_NAME));