You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2021/09/23 02:49:39 UTC
[james-project] branch 3.6.x updated: JAMES-3646 [3.6.1] Prevent
directory traversal on top of maildir mailbox (#659)
This is an automated email from the ASF dual-hosted git repository.
btellier pushed a commit to branch 3.6.x
in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/3.6.x by this push:
new c019c34 JAMES-3646 [3.6.1] Prevent directory traversal on top of maildir mailbox (#659)
c019c34 is described below
commit c019c349f9cb2dc6e767e71ea26cb1789ec482f9
Author: Benoit TELLIER <bt...@linagora.com>
AuthorDate: Thu Sep 23 09:49:35 2021 +0700
JAMES-3646 [3.6.1] Prevent directory traversal on top of maildir mailbox (#659)
---
.../james/mailbox/maildir/MaildirFolder.java | 12 ++++
.../apache/james/mailbox/maildir/MaildirStore.java | 40 +++++++++++--
.../mailbox/maildir/mail/MaildirMailboxMapper.java | 4 +-
.../mailbox/maildir/DirectoryTraversalTest.java | 65 ++++++++++++++++++++++
4 files changed, 113 insertions(+), 8 deletions(-)
diff --git a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java
index e4f56bb..455f053 100644
--- a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java
+++ b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java
@@ -49,6 +49,7 @@ import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.MessageUid;
import org.apache.james.mailbox.ModSeq;
import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.exception.MailboxNotFoundException;
import org.apache.james.mailbox.model.MailboxACL;
import org.apache.james.mailbox.model.MailboxACL.EntryKey;
import org.apache.james.mailbox.model.MailboxACL.Rfc4314Rights;
@@ -104,6 +105,17 @@ public class MaildirFolder {
this.lastUid = Optional.empty();
}
+ public MaildirFolder validateWithinFolder(File maildirRoot) throws MailboxNotFoundException {
+ try {
+ if (!rootFolder.getCanonicalPath().startsWith(maildirRoot.getCanonicalPath())) {
+ throw new MailboxNotFoundException(rootFolder.getCanonicalPath() + " jail breaks out of " + maildirRoot.getCanonicalPath());
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return this;
+ }
+
private MaildirMessageName newMaildirMessageName(MaildirFolder folder, String fullName) {
MaildirMessageName mdn = new MaildirMessageName(folder, fullName);
mdn.setMessageNameStrictParse(isMessageNameStrictParse());
diff --git a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirStore.java b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirStore.java
index 18a81c7..1713b5d 100644
--- a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirStore.java
+++ b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirStore.java
@@ -83,8 +83,10 @@ public class MaildirStore implements UidProvider, ModSeqProvider {
*
* @return The MaildirFolder
*/
- public MaildirFolder createMaildirFolder(Mailbox mailbox) {
- MaildirFolder mf = new MaildirFolder(getFolderName(mailbox), mailbox.generateAssociatedPath(), locker);
+ public MaildirFolder createMaildirFolder(Mailbox mailbox) throws MailboxNotFoundException {
+ MaildirFolder mf = new MaildirFolder(getFolderName(mailbox), mailbox.generateAssociatedPath(), locker)
+ .validateWithinFolder(getMaildirRoot())
+ .validateWithinFolder(new File(userRoot(mailbox.getUser())));
mf.setMessageNameStrictParse(isMessageNameStrictParse());
return mf;
}
@@ -110,9 +112,10 @@ public class MaildirStore implements UidProvider, ModSeqProvider {
* @throws MailboxNotFoundException If the mailbox folder doesn't exist
* @throws MailboxException If the mailbox folder can't be read
*/
- public Mailbox loadMailbox(MailboxSession session, MailboxPath mailboxPath)
- throws MailboxNotFoundException, MailboxException {
- MaildirFolder folder = new MaildirFolder(getFolderName(mailboxPath), mailboxPath, locker);
+ public Mailbox loadMailbox(MailboxSession session, MailboxPath mailboxPath) throws MailboxNotFoundException, MailboxException {
+ MaildirFolder folder = new MaildirFolder(getFolderName(mailboxPath), mailboxPath, locker)
+ .validateWithinFolder(getMaildirRoot())
+ .validateWithinFolder(new File(userRoot(session.getUser())));
folder.setMessageNameStrictParse(isMessageNameStrictParse());
if (!folder.exists()) {
throw new MailboxNotFoundException(mailboxPath);
@@ -120,6 +123,16 @@ public class MaildirStore implements UidProvider, ModSeqProvider {
return loadMailbox(session, folder.getRootFile(), mailboxPath);
}
+ public Mailbox loadMailboxNoUserCheck(MailboxSession session, MailboxPath mailboxPath) throws MailboxNotFoundException, MailboxException {
+ MaildirFolder folder = new MaildirFolder(getFolderName(mailboxPath), mailboxPath, locker)
+ .validateWithinFolder(getMaildirRoot());
+ folder.setMessageNameStrictParse(isMessageNameStrictParse());
+ if (!folder.exists()) {
+ throw new MailboxNotFoundException(mailboxPath);
+ }
+ return loadMailboxNoChecks(session, folder.getRootFile(), mailboxPath);
+ }
+
/**
* Creates a Mailbox object with data loaded from the file system
* @param mailboxFile File object referencing the folder for the mailbox
@@ -128,7 +141,22 @@ public class MaildirStore implements UidProvider, ModSeqProvider {
* @throws MailboxException If the mailbox folder doesn't exist or can't be read
*/
private Mailbox loadMailbox(MailboxSession session, File mailboxFile, MailboxPath mailboxPath) throws MailboxException {
- MaildirFolder folder = new MaildirFolder(mailboxFile.getAbsolutePath(), mailboxPath, locker);
+ MaildirFolder folder = new MaildirFolder(mailboxFile.getAbsolutePath(), mailboxPath, locker)
+ .validateWithinFolder(getMaildirRoot())
+ .validateWithinFolder(new File(userRoot(session.getUser())));
+ folder.setMessageNameStrictParse(isMessageNameStrictParse());
+ try {
+ Mailbox loadedMailbox = new Mailbox(mailboxPath, folder.getUidValidity(), folder.readMailboxId());
+ loadedMailbox.setACL(folder.getACL());
+ return loadedMailbox;
+ } catch (IOException e) {
+ throw new MailboxException("Unable to load Mailbox " + mailboxPath, e);
+ }
+ }
+
+ private Mailbox loadMailboxNoChecks(MailboxSession session, File mailboxFile, MailboxPath mailboxPath) throws MailboxException {
+ MaildirFolder folder = new MaildirFolder(mailboxFile.getAbsolutePath(), mailboxPath, locker)
+ .validateWithinFolder(getMaildirRoot());
folder.setMessageNameStrictParse(isMessageNameStrictParse());
try {
Mailbox loadedMailbox = new Mailbox(mailboxPath, folder.getUidValidity(), folder.readMailboxId());
diff --git a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMailboxMapper.java b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMailboxMapper.java
index edadc5c..80668cd 100644
--- a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMailboxMapper.java
+++ b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMailboxMapper.java
@@ -290,7 +290,7 @@ public class MaildirMailboxMapper extends NonTransactionalMapper implements Mail
MailboxPath inboxMailboxPath = MailboxPath.forUser(Username.of(userName), MailboxConstants.INBOX);
try {
- mailboxList.add(maildirStore.loadMailbox(session, inboxMailboxPath));
+ mailboxList.add(maildirStore.loadMailboxNoUserCheck(session, inboxMailboxPath));
} catch (MailboxException e) {
//do nothing, we should still be able to list the mailboxes even if INBOX does not exist
}
@@ -301,7 +301,7 @@ public class MaildirMailboxMapper extends NonTransactionalMapper implements Mail
for (File mailbox: mailboxes) {
MailboxPath mailboxPath = MailboxPath.forUser(Username.of(userName),
mailbox.getName().substring(1));
- mailboxList.add(maildirStore.loadMailbox(session, mailboxPath));
+ mailboxList.add(maildirStore.loadMailboxNoUserCheck(session, mailboxPath));
}
}
diff --git a/mailbox/maildir/src/test/java/org/apache/james/mailbox/maildir/DirectoryTraversalTest.java b/mailbox/maildir/src/test/java/org/apache/james/mailbox/maildir/DirectoryTraversalTest.java
new file mode 100644
index 0000000..d8dfeb3
--- /dev/null
+++ b/mailbox/maildir/src/test/java/org/apache/james/mailbox/maildir/DirectoryTraversalTest.java
@@ -0,0 +1,65 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+package org.apache.james.mailbox.maildir;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.io.File;
+import java.util.UUID;
+
+import org.apache.james.core.Username;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.store.StoreMailboxManager;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class DirectoryTraversalTest {
+ StoreMailboxManager mailboxManager;
+
+ @BeforeEach
+ void setUp() {
+ mailboxManager = MaildirMailboxManagerProvider.createMailboxManager("/%fulluser",
+ new File(System.getProperty("java.io.tmpdir") + "/" + UUID.randomUUID()));
+ }
+
+ @Test
+ void directoryTraversalUsingUsernameFails() {
+ MailboxSession session = mailboxManager.createSystemSession(Username.of("../bob"));
+
+ assertThatThrownBy(() -> mailboxManager.createMailbox(MailboxPath.inbox(session), session))
+ .hasMessageContaining("jail breaks out of");
+ }
+
+ @Test
+ void directoryTraversalUsingMailboxName1Fails() {
+ MailboxSession session = mailboxManager.createSystemSession(Username.of("bob"));
+
+ assertThatThrownBy(() -> mailboxManager.createMailbox(MailboxPath.forUser(session.getUser(), "./alice/box2"), session))
+ .isNotNull();
+ }
+
+ @Test
+ void directoryTraversalUsingMailboxName2Fails() {
+ MailboxSession session = mailboxManager.createSystemSession(Username.of("bob"));
+
+ assertThatThrownBy(() -> mailboxManager.createMailbox(MailboxPath.forUser(session.getUser(), "alice/../box2"), session))
+ .isNotNull();
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org