From 0eb671e7cb4324437780e64a9d23cd66baf6b3ff Mon Sep 17 00:00:00 2001
From: Chris Ridd <chris.ridd@forgerock.com>
Date: Wed, 08 Aug 2012 16:30:08 +0000
Subject: [PATCH] Fix OPENDJ-558 Validation for JPEGSyntax values

---
 opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java  |   30 ++++++++++++++-
 opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/JPEGSyntaxImpl.java |   44 ++++++++++++++++++++--
 opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java         |   39 ++++++++++++++++++-
 3 files changed, 105 insertions(+), 8 deletions(-)

diff --git a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/JPEGSyntaxImpl.java b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/JPEGSyntaxImpl.java
index 72d7c96..2195e84 100644
--- a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/JPEGSyntaxImpl.java
+++ b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/JPEGSyntaxImpl.java
@@ -22,6 +22,7 @@
  *
  *
  *      Copyright 2009 Sun Microsystems, Inc.
+ *      Portions Copyright 2012 ForgeRock AS
  */
 
 package org.forgerock.opendj.ldap.schema;
@@ -34,9 +35,10 @@
 import org.forgerock.opendj.ldap.ByteSequence;
 
 /**
- * This class implements the JPEG attribute syntax. This should be restricted to
- * holding only JPEG image contents, but we will accept any set of bytes. It
- * will be treated much like the octet string attribute syntax.
+ * This class implements the JPEG attribute syntax. This is actually
+ * two specifications - JPEG and JFIF. As an extension we allow JPEG
+ * and Exif, which is what most digital cameras use. We only check for
+ * valid JFIF and Exif headers.
  */
 final class JPEGSyntaxImpl extends AbstractSyntaxImpl {
     @Override
@@ -73,7 +75,41 @@
      */
     public boolean valueIsAcceptable(final Schema schema, final ByteSequence value,
             final LocalizableMessageBuilder invalidReason) {
-        // All values will be acceptable for the fax syntax.
+
+        if (!schema.allowMalformedJPEGPhotos()) {
+            /* JFIF files start:
+             * 0xff 0xd8 0xff 0xe0 LH LL 0x4a 0x46 0x49 0x46 ...
+             * SOI       APP0      len   "JFIF"
+             *
+             * Exif files (from most digital cameras) start:
+             * 0xff 0xd8 0xff 0xe1 LH LL 0x45 0x78 0x69 0x66 ...
+             * SOI       APP1      len   "Exif"
+             *
+             * So all legal values must be at least 10 bytes long
+             */
+            if (value.length() < 10) {
+                return false;
+            }
+
+            if (value.byteAt(0) != (byte) 0xff && value.byteAt(1) != (byte) 0xd8) {
+                return false;
+            }
+
+            if (value.byteAt(2) == (byte) 0xff && value.byteAt(3) == (byte) 0xe0
+                && value.byteAt(6) == 'J' && value.byteAt(7) == 'F'
+                && value.byteAt(8) == 'I' && value.byteAt(9) == 'F') {
+                return true;
+            }
+
+            if (value.byteAt(2) == (byte) 0xff && value.byteAt(3) == (byte) 0xe1
+                && value.byteAt(6) == 'E' && value.byteAt(7) == 'x'
+                && value.byteAt(8) == 'i' && value.byteAt(9) == 'f') {
+                return true;
+            }
+
+            // No JFIF or Exif header found
+            return false;
+        }
         return true;
     }
 }
diff --git a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java
index 2c460c8..ad4db1e 100644
--- a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java
+++ b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java
@@ -85,6 +85,10 @@
             return true;
         }
 
+        public boolean allowMalformedJPEGPhotos() {
+            return true;
+        }
+
         public boolean allowZeroLengthDirectoryStrings() {
             return false;
         }
@@ -301,6 +305,8 @@
     private static interface Impl {
         boolean allowMalformedNamesAndOptions();
 
+        boolean allowMalformedJPEGPhotos();
+
         boolean allowNonStandardTelephoneNumbers();
 
         boolean allowZeroLengthDirectoryStrings();
@@ -393,6 +399,10 @@
             return strictImpl.allowMalformedNamesAndOptions();
         }
 
+        public boolean allowMalformedJPEGPhotos() {
+            return strictImpl.allowMalformedJPEGPhotos();
+        }
+
         public boolean allowNonStandardTelephoneNumbers() {
             return strictImpl.allowNonStandardTelephoneNumbers();
         }
@@ -651,6 +661,8 @@
 
         private final String schemaName;
 
+        private final boolean allowMalformedJPEGPhotos;
+
         private final boolean allowNonStandardTelephoneNumbers;
 
         private final boolean allowZeroLengthDirectoryStrings;
@@ -658,6 +670,7 @@
         private final boolean allowMalformedNamesAndOptions;
 
         StrictImpl(final String schemaName, final boolean allowMalformedNamesAndOptions,
+                final boolean allowMalformedJPEGPhotos,
                 final boolean allowNonStandardTelephoneNumbers,
                 final boolean allowZeroLengthDirectoryStrings,
                 final Map<String, Syntax> numericOID2Syntaxes,
@@ -680,6 +693,7 @@
                 final List<LocalizableMessage> warnings) {
             this.schemaName = schemaName;
             this.allowMalformedNamesAndOptions = allowMalformedNamesAndOptions;
+            this.allowMalformedJPEGPhotos = allowMalformedJPEGPhotos;
             this.allowNonStandardTelephoneNumbers = allowNonStandardTelephoneNumbers;
             this.allowZeroLengthDirectoryStrings = allowZeroLengthDirectoryStrings;
             this.numericOID2Syntaxes = Collections.unmodifiableMap(numericOID2Syntaxes);
@@ -707,6 +721,10 @@
             return allowMalformedNamesAndOptions;
         }
 
+        public boolean allowMalformedJPEGPhotos() {
+            return allowMalformedJPEGPhotos;
+        }
+
         public boolean allowNonStandardTelephoneNumbers() {
             return allowNonStandardTelephoneNumbers;
         }
@@ -1323,6 +1341,7 @@
     private final Impl impl;
 
     Schema(final String schemaName, final boolean allowMalformedNamesAndOptions,
+            final boolean allowMalformedJPEGPhotos,
             final boolean allowNonStandardTelephoneNumbers,
             final boolean allowZeroLengthDirectoryStrings,
             final Map<String, Syntax> numericOID2Syntaxes,
@@ -1345,8 +1364,9 @@
             final List<LocalizableMessage> warnings) {
         impl =
                 new StrictImpl(schemaName, allowMalformedNamesAndOptions,
-                        allowNonStandardTelephoneNumbers, allowZeroLengthDirectoryStrings,
-                        numericOID2Syntaxes, numericOID2MatchingRules, numericOID2MatchingRuleUses,
+                        allowMalformedJPEGPhotos, allowNonStandardTelephoneNumbers,
+                        allowZeroLengthDirectoryStrings, numericOID2Syntaxes,
+                        numericOID2MatchingRules, numericOID2MatchingRuleUses,
                         numericOID2AttributeTypes, numericOID2ObjectClasses, numericOID2NameForms,
                         numericOID2ContentRules, id2StructureRules, name2MatchingRules,
                         name2MatchingRuleUses, name2AttributeTypes, name2ObjectClasses,
@@ -1382,6 +1402,21 @@
     }
 
     /**
+     * Returns {@code true} if the JPEG Photo syntax defined for this
+     * schema allows values which do not conform to the JFIF or Exif
+     * specifications.
+     * <p>
+     * By default this compatibility option is set to {@code true}.
+     *
+     * @return {@code true} if the JPEG Photo syntax defined for this
+     *         schema allows values which do not conform to the JFIF
+     *         of Exit specifications.
+     */
+    public boolean allowMalformedJPEGPhotos() {
+        return impl.allowMalformedJPEGPhotos();
+    }
+
+    /**
      * Returns {@code true} if the Telephone Number syntax defined for this
      * schema allows values which do not conform to the E.123 international
      * telephone number format.
diff --git a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
index 4308e87..33e9ae8 100644
--- a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
+++ b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
@@ -172,6 +172,8 @@
 
     private boolean allowMalformedNamesAndOptions;
 
+    private boolean allowMalformedJPEGPhotos;
+
     // A schema which should be copied into this builder on any mutation.
     private Schema copyOnWriteSchema = null;
 
@@ -2247,6 +2249,26 @@
     }
 
     /**
+     * Specifies whether or not the JPEG Photo syntax should allow values
+     * which do not conform to the JFIF or Exif specifications.
+     * <p>
+     * By default this compatibility option is set to {@code true}.
+     *
+     * @param allowMalformedJPEGPhotos
+     *            {@code true} if the JPEG Photo syntax should allow
+     *            values which do not conform to the JFIF or Exif
+     *            specifications.
+     * @return A reference to this {@code SchemaBuilder}.
+     */
+    public SchemaBuilder allowMalformedJPEGPhotos(
+            final boolean allowMalformedJPEGPhotos) {
+        lazyInitBuilder();
+
+        this.allowMalformedJPEGPhotos = allowMalformedJPEGPhotos;
+        return this;
+    }
+
+    /**
      * Specifies whether or not the Telephone Number syntax should allow values
      * which do not conform to the E.123 international telephone number format.
      * <p>
@@ -2508,8 +2530,9 @@
 
         final Schema schema =
                 new Schema(localSchemaName, allowMalformedNamesAndOptions,
-                        allowNonStandardTelephoneNumbers, allowZeroLengthDirectoryStrings,
-                        numericOID2Syntaxes, numericOID2MatchingRules, numericOID2MatchingRuleUses,
+                        allowMalformedJPEGPhotos, allowNonStandardTelephoneNumbers,
+                        allowZeroLengthDirectoryStrings, numericOID2Syntaxes,
+                        numericOID2MatchingRules, numericOID2MatchingRuleUses,
                         numericOID2AttributeTypes, numericOID2ObjectClasses, numericOID2NameForms,
                         numericOID2ContentRules, id2StructureRules, name2MatchingRules,
                         name2MatchingRuleUses, name2AttributeTypes, name2ObjectClasses,
@@ -2784,6 +2807,7 @@
         // Lazy initialization.
         if (numericOID2Syntaxes == null) {
             allowMalformedNamesAndOptions = true;
+            allowMalformedJPEGPhotos = true;
             allowNonStandardTelephoneNumbers = true;
             allowZeroLengthDirectoryStrings = false;
 
@@ -2814,6 +2838,7 @@
             addSchema0(copyOnWriteSchema, true);
 
             allowMalformedNamesAndOptions = copyOnWriteSchema.allowMalformedNamesAndOptions();
+            allowMalformedJPEGPhotos = copyOnWriteSchema.allowMalformedJPEGPhotos();
             allowNonStandardTelephoneNumbers = copyOnWriteSchema.allowNonStandardTelephoneNumbers();
             allowZeroLengthDirectoryStrings = copyOnWriteSchema.allowZeroLengthDirectoryStrings();
 
@@ -2826,6 +2851,7 @@
         this.copyOnWriteSchema = copyOnWriteSchema;
 
         this.allowMalformedNamesAndOptions = true;
+        this.allowMalformedJPEGPhotos = true;
         this.allowNonStandardTelephoneNumbers = true;
         this.allowZeroLengthDirectoryStrings = false;
 

--
Gitblit v1.10.0