Merge remote-tracking branch 'goog/security-aosp-mnc-mr1-release' into HEAD
diff --git a/luni/src/main/java/libcore/net/url/FtpURLConnection.java b/luni/src/main/java/libcore/net/url/FtpURLConnection.java
index 021bfa2..a2c5151 100644
--- a/luni/src/main/java/libcore/net/url/FtpURLConnection.java
+++ b/luni/src/main/java/libcore/net/url/FtpURLConnection.java
@@ -503,7 +503,30 @@
         }
     }
 
+    // @VisibleForTesting
+    public static void validateCommand(String command) throws IOException {
+        final int terminatorIdx = command.length() - 2;
+
+        // First check that the command is terminated correctly, it must
+        // always end with '<CR><LF>'.
+        if (terminatorIdx < 0 ||
+            command.charAt(terminatorIdx) != '\r' ||
+            command.charAt(terminatorIdx + 1) != '\n') {
+            throw new IOException("Command terminated incorrectly");
+        }
+
+        // Then check that there are no <CR><LF> characters elsewhere in the
+        // command.
+        //
+        // NOTE: If we're being really pedantic, we'll need to check that this
+        // is the lower half of ASCII as well.
+        if (command.indexOf('\r') < terminatorIdx || command.indexOf('\n') < terminatorIdx) {
+            throw new IOException("Invalid character in command");
+        }
+    }
+
     private void write(String command) throws IOException {
+        validateCommand(command);
         ctrlOutput.write(command.getBytes(StandardCharsets.ISO_8859_1));
     }
 }
diff --git a/luni/src/test/java/libcore/net/url/FtpURLConnectionTest.java b/luni/src/test/java/libcore/net/url/FtpURLConnectionTest.java
new file mode 100644
index 0000000..e2c24eb
--- /dev/null
+++ b/luni/src/test/java/libcore/net/url/FtpURLConnectionTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed 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 libcore.net.url;
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import libcore.net.url.FtpURLConnection;
+
+public class FtpURLConnectionTest extends TestCase {
+    public void testValidateCommand() throws Exception {
+        FtpURLConnection.validateCommand("\r\n");
+        FtpURLConnection.validateCommand("USER foo\r\n");
+
+        try {
+            FtpURLConnection.validateCommand("\r");
+            fail();
+        } catch (IOException expected) {
+        }
+
+        try {
+            FtpURLConnection.validateCommand("\n");
+            fail();
+        } catch (IOException expected) {
+        }
+
+        try {
+            FtpURLConnection.validateCommand("USER foo\rbar\r\n");
+            fail();
+        } catch (IOException expected) {
+        }
+
+        try {
+            FtpURLConnection.validateCommand("USER foo\nbar\r\n");
+            fail();
+        } catch (IOException expected) {
+        }
+
+        try {
+            FtpURLConnection.validateCommand("USER foo\r\nbar\r\n");
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+}