From fbff86d38ef3f7ce639e5688ea3a4ed76b9abce0 Mon Sep 17 00:00:00 2001 From: klasdjw Date: Tue, 29 Jul 2025 07:39:16 +0800 Subject: [PATCH] Fix CVE-2025-7962 (cherry picked from commit 3ba1e13dd0a2ae6a32b92d0e5e2056839552eb59) --- CVE-2025-7962.patch | 318 ++++++++++++++++++++++++++++++++++++++++++++ jakarta-mail.spec | 8 +- 2 files changed, 324 insertions(+), 2 deletions(-) create mode 100644 CVE-2025-7962.patch diff --git a/CVE-2025-7962.patch b/CVE-2025-7962.patch new file mode 100644 index 0000000..d65facb --- /dev/null +++ b/CVE-2025-7962.patch @@ -0,0 +1,318 @@ +From cc9b954f3816f18f1b96dd50b1f8f51b3116462d Mon Sep 17 00:00:00 2001 +From: Jorge Bescos Gascon +Date: Thu, 17 Jul 2025 10:56:52 +0200 +Subject: [PATCH] Issue 299 (#760) + +* Issue 299 + +Signed-off-by: Jorge Bescos Gascon + +* Fix TCK + +Signed-off-by: Jorge Bescos Gascon + +--------- + +Signed-off-by: Jorge Bescos Gascon +--- + docker/run_jakartamailtck.sh | 6 +- + .../java/com/sun/mail/smtp/SMTPTransport.java | 15 ++- + .../com/sun/mail/test/FakeSMTPServer.java | 87 ++++++++++++++ + .../java/com/sun/mail/test/Issue299Test.java | 107 ++++++++++++++++++ + pom.xml | 3 +- + 5 files changed, 212 insertions(+), 6 deletions(-) + create mode 100644 mail/src/test/java/com/sun/mail/test/FakeSMTPServer.java + create mode 100644 mail/src/test/java/com/sun/mail/test/Issue299Test.java + +diff --git a/docker/run_jakartamailtck.sh b/docker/run_jakartamailtck.sh +index 941fe6f19..d3f347c34 100755 +--- a/docker/run_jakartamailtck.sh ++++ b/docker/run_jakartamailtck.sh +@@ -1,6 +1,6 @@ + #!/bin/bash -xe + # +-# Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. ++# Copyright (c) 2018, 2025 Oracle and/or its affiliates. All rights reserved. + # + # This program and the accompanying materials are made available under the + # terms of the Eclipse Public License v. 2.0, which is available at +@@ -17,10 +17,10 @@ + WGET_PROPS="-q --no-cache" + # JAF is not really needed when running on JDK 1.8 + if [ -z "$JAF_BUNDLE_URL" ];then +- export JAF_BUNDLE_URL=http://central.maven.org/maven2/com/sun/activation/jakarta.activation/1.2.1/jakarta.activation-1.2.1.jar ++ export JAF_BUNDLE_URL=https://repo1.maven.org/maven2/com/sun/activation/jakarta.activation/1.2.1/jakarta.activation-1.2.1.jar + fi + if [ -z "$MAIL_TCK_BUNDLE_URL" ];then +- export MAIL_TCK_BUNDLE_URL=https://jenkins.eclipse.org/mail/job/mail-tck/job/master/lastSuccessfulBuild/artifact/bundles/mail-tck-1.6.0.zip ++ export MAIL_TCK_BUNDLE_URL=https://download.eclipse.org/jakartaee/mail/1.6/eclipse-mail-tck-1.6.0.zip + fi + wget $WGET_PROPS $JAF_BUNDLE_URL -O jakarta.activation.jar + wget $WGET_PROPS $MAIL_TCK_BUNDLE_URL -O mailtck.zip +diff --git a/mail/src/main/java/com/sun/mail/smtp/SMTPTransport.java b/mail/src/main/java/com/sun/mail/smtp/SMTPTransport.java +index efbab723a..1bc480d36 100644 +--- a/mail/src/main/java/com/sun/mail/smtp/SMTPTransport.java ++++ b/mail/src/main/java/com/sun/mail/smtp/SMTPTransport.java +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1997, 2025 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at +@@ -2424,14 +2424,25 @@ private void sendCommand(byte[] cmdBytes) throws MessagingException { + //logger.fine("SENT: " + new String(cmdBytes, 0)); + + try { ++ validateCommand(cmdBytes); + serverOutput.write(cmdBytes); + serverOutput.write(CRLF); + serverOutput.flush(); +- } catch (IOException ex) { ++ } catch (IOException | RuntimeException ex) { + throw new MessagingException("Can't send command to SMTP host", ex); + } + } + ++ private void validateCommand(byte[] cmdBytes) throws MessagingException { ++ final byte CR = '\r'; ++ final byte LF = '\n'; ++ for (byte b : cmdBytes) { ++ if (b == LF || b == CR) { ++ throw new IllegalArgumentException("Command contains illegal character: " + String.format("0x%02x",b)); ++ } ++ } ++ } ++ + /** + * Reads server reponse returning the returnCode + * as the number. Returns -1 on failure. Sets +diff --git a/mail/src/test/java/com/sun/mail/test/FakeSMTPServer.java b/mail/src/test/java/com/sun/mail/test/FakeSMTPServer.java +new file mode 100644 +index 000000000..aca9f584a +--- /dev/null ++++ b/mail/src/test/java/com/sun/mail/test/FakeSMTPServer.java +@@ -0,0 +1,87 @@ ++/* ++ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved. ++ * ++ * This program and the accompanying materials are made available under the ++ * terms of the Eclipse Public License v. 2.0, which is available at ++ * http://www.eclipse.org/legal/epl-2.0. ++ * ++ * This Source Code may also be made available under the following Secondary ++ * Licenses when the conditions for such availability set forth in the ++ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, ++ * version 2 with the GNU Classpath Exception, which is available at ++ * https://www.gnu.org/software/classpath/license.html. ++ * ++ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ++ */ ++ ++package com.sun.mail.test; ++ ++import java.io.BufferedReader; ++import java.io.IOException; ++import java.io.InputStreamReader; ++import java.io.PrintWriter; ++import java.net.ServerSocket; ++import java.net.Socket; ++ ++public class FakeSMTPServer { ++ ++ private volatile boolean running = true; ++ ++ public void start(int port) { ++ try (ServerSocket serverSocket = new ServerSocket(port)) { ++ System.out.println("Fake SMTP server is running on port " + port); ++ ++ while (running) { ++ // 等待客户端连接 ++ Socket clientSocket = serverSocket.accept(); ++ System.out.println("New client connected: " + clientSocket.getInetAddress()); ++ ++ // 获取客户端输入流,用于接收 SMTP 请求 ++ BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); ++ PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true); ++ ++ // 发送欢迎消息,模拟服务器响应 ++ writer.println("220 Fake SMTP Server Ready"); ++ ++ // 读取客户端的输入并打印 ++ String line; ++ while ((line = reader.readLine()) != null) { ++ // 打印收到的每一行 SMTP 报文 ++ System.out.println("Received: " + line); ++ ++ // 模拟 SMTP 交互 ++ if (line.startsWith("HELO") || line.startsWith("EHLO")) { ++ writer.println("250 Hello " + clientSocket.getInetAddress().getHostName()); ++ } else if (line.startsWith("MAIL FROM")) { ++ writer.println("250 OK"); ++ } else if (line.startsWith("RCPT TO")) { ++ writer.println("250 OK"); ++ } else if (line.equals("DATA")) { ++ writer.println("354 Start mail input; end with ."); ++ } else if (line.equals(".")) { ++ writer.println("250 OK: Message received"); ++ } else if (line.equals("QUIT")) { ++ writer.println("221 Bye"); ++ break; ++ } ++ ++ // 如果客户端发送了 "QUIT",则退出当前会话 ++ if (line.equals("QUIT")) { ++ break; ++ } ++ } ++ ++ // 关闭当前客户端连接 ++ clientSocket.close(); ++ System.out.println("Client disconnected"); ++ } ++ } catch (IOException e) { ++ e.printStackTrace(); ++ } ++ } ++ ++ public void stop() { ++ running = false; ++ System.out.println("Fake SMTP server is stopping..."); ++ } ++} +\ No newline at end of file +diff --git a/mail/src/test/java/com/sun/mail/test/Issue299Test.java b/mail/src/test/java/com/sun/mail/test/Issue299Test.java +new file mode 100644 +index 000000000..71d31ee75 +--- /dev/null ++++ b/mail/src/test/java/com/sun/mail/test/Issue299Test.java +@@ -0,0 +1,107 @@ ++/* ++ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved. ++ * ++ * This program and the accompanying materials are made available under the ++ * terms of the Eclipse Public License v. 2.0, which is available at ++ * http://www.eclipse.org/legal/epl-2.0. ++ * ++ * This Source Code may also be made available under the following Secondary ++ * Licenses when the conditions for such availability set forth in the ++ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, ++ * version 2 with the GNU Classpath Exception, which is available at ++ * https://www.gnu.org/software/classpath/license.html. ++ * ++ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ++ */ ++ ++package com.sun.mail.test; ++ ++import static org.junit.Assert.assertEquals; ++import static org.junit.Assert.fail; ++ ++import java.io.IOException; ++import java.net.InetSocketAddress; ++import java.net.Socket; ++import java.net.UnknownHostException; ++import java.util.Properties; ++import java.util.concurrent.ExecutorService; ++import java.util.concurrent.Executors; ++import java.util.concurrent.TimeUnit; ++ ++import javax.mail.Authenticator; ++import javax.mail.MessagingException; ++import javax.mail.PasswordAuthentication; ++import javax.mail.Session; ++import javax.mail.Transport; ++import javax.mail.internet.InternetAddress; ++import javax.mail.internet.MimeMessage; ++ ++import org.junit.Test; ++ ++ ++public class Issue299Test { ++ ++ static final int PORT = 18465; ++ static final String USER = "xxxunj******@163.com"; ++ static final String PASSWD = "EY**************"; ++ static final String toMail = "甲申申甶甴甸电甹甸甸畀畱畱瘮畣畯畭甾瘍瘊畄畁畔畁瘍瘊畓畵畢番略畣畴町畐畗畎畅畄瘍瘊瘍瘊畉瘠界畏畖畅瘠留畏畕瘡瘍瘊瘮瘍瘊畑畕畉畔瘍瘊@qq.com"; ++ static String TEXT = "Hello world"; ++ static String SUBJECT = "Test"; ++ ++ static Properties properties = new Properties(); ++ static Authenticator authenticator = null; ++ ++ public static void initProp(){ ++ properties.setProperty("mail.host","127.0.0.1"); ++ properties.put("mail.debug", "true"); ++ properties.setProperty("mail.transport.protocol","smtp"); ++ properties.setProperty("mail.smtp.auth","true"); ++ properties.setProperty("mail.smtp.timeout", "1000"); ++ properties.setProperty("mail.smtp.port", Integer.toString(PORT)); ++ authenticator = new Authenticator() { ++ @Override ++ protected PasswordAuthentication getPasswordAuthentication() { ++ return new PasswordAuthentication(USER, PASSWD); ++ } ++ }; ++ } ++ public static Session getSession(){ ++ return Session.getInstance(properties, authenticator); ++ } ++ ++ @Test ++ public void test() throws Exception { ++ FakeSMTPServer server = new FakeSMTPServer(); ++ ExecutorService service = Executors.newFixedThreadPool(1); ++ try { ++ service.execute(() -> { ++ try { ++ server.start(PORT); ++ Thread.sleep(1000); ++ } catch (Exception e) { ++ e.printStackTrace(); ++ } ++ }); ++ ++ initProp(); ++ ++ Session session = getSession(); ++ MimeMessage msg = new MimeMessage(session); ++ msg.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(toMail)); ++ msg.setSubject(SUBJECT); ++ msg.setContent(TEXT, "text/html;charset=utf-8"); ++ msg.saveChanges(); ++ Transport.send(msg); ++ fail("It is expected an IllegalArgumentException because there are illegal characters in the command"); ++ } catch (Exception e) { ++ if (e.getCause() == null || e.getCause().getClass() != IllegalArgumentException.class) { ++ fail("It is expected an IllegalArgumentException because there are illegal characters in the command. Exception was: " + e); ++ } ++ } finally { ++ server.stop(); ++ service.shutdown(); ++ service.awaitTermination(5, TimeUnit.SECONDS); ++ } ++ } ++ ++} +\ No newline at end of file +diff --git a/pom.xml b/pom.xml +index 34d8211ef..91989d14b 100644 +--- a/pom.xml ++++ b/pom.xml +@@ -1,7 +1,7 @@ + +