Don't use strcasecmp and strncasecmp for IA5 strings
[openssl.git] / crypto / x509v3 / v3_ncons.c
index 561128e3faddeb8bef86fd90a8e5e135e4373db5..1f1996b68ef0aeb771c745977c5adfa62895ed21 100644 (file)
@@ -7,9 +7,8 @@
  * https://www.openssl.org/source/license.html
  */
 
-#include "e_os.h"               /* for strncasecmp */
 #include "internal/cryptlib.h"
-#include <limits.h>
+#include "internal/numbers.h"
 #include <stdio.h>
 #include "internal/asn1_int.h"
 #include <openssl/asn1t.h>
@@ -64,6 +63,46 @@ ASN1_SEQUENCE(NAME_CONSTRAINTS) = {
 IMPLEMENT_ASN1_ALLOC_FUNCTIONS(GENERAL_SUBTREE)
 IMPLEMENT_ASN1_ALLOC_FUNCTIONS(NAME_CONSTRAINTS)
 
+/*
+ * We cannot use strncasecmp here because that applies locale specific rules.
+ * For example in Turkish 'I' is not the uppercase character for 'i'. We need to
+ * do a simple ASCII case comparison ignoring the locale (that is why we use
+ * numeric constants below).
+ */
+static int ia5ncasecmp(const char *s1, const char *s2, size_t n)
+{
+    for (; n > 0; n--, s1++, s2++) {
+        if (*s1 != *s2) {
+            unsigned char c1 = (unsigned char)*s1, c2 = (unsigned char)*s2;
+
+            /* Convert to lower case */
+            if (c1 >= 0x41 /* A */ && c1 <= 0x5A /* Z */)
+                c1 += 0x20;
+            if (c2 >= 0x41 /* A */ && c2 <= 0x5A /* Z */)
+                c2 += 0x20;
+
+            if (c1 == c2)
+                continue;
+
+            if (c1 < c2)
+                return -1;
+
+            /* c1 > c2 */
+            return 1;
+        } else if (*s1 == 0) {
+            /* If we get here we know that *s2 == 0 too */
+            return 0;
+        }
+    }
+
+    return 0;
+}
+
+static int ia5casecmp(const char *s1, const char *s2)
+{
+    return ia5ncasecmp(s1, s2, SIZE_MAX);
+}
+
 static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method,
                                   X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval)
 {
@@ -418,7 +457,7 @@ static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base)
             return X509_V_ERR_PERMITTED_VIOLATION;
     }
 
-    if (strcasecmp(baseptr, dnsptr))
+    if (ia5casecmp(baseptr, dnsptr))
         return X509_V_ERR_PERMITTED_VIOLATION;
 
     return X509_V_OK;
@@ -438,7 +477,7 @@ static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base)
     if (!baseat && (*baseptr == '.')) {
         if (eml->length > base->length) {
             emlptr += eml->length - base->length;
-            if (strcasecmp(baseptr, emlptr) == 0)
+            if (ia5casecmp(baseptr, emlptr) == 0)
                 return X509_V_OK;
         }
         return X509_V_ERR_PERMITTED_VIOLATION;
@@ -459,7 +498,7 @@ static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base)
     }
     emlptr = emlat + 1;
     /* Just have hostname left to match: case insensitive */
-    if (strcasecmp(baseptr, emlptr))
+    if (ia5casecmp(baseptr, emlptr))
         return X509_V_ERR_PERMITTED_VIOLATION;
 
     return X509_V_OK;
@@ -498,14 +537,14 @@ static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base)
     if (*baseptr == '.') {
         if (hostlen > base->length) {
             p = hostptr + hostlen - base->length;
-            if (strncasecmp(p, baseptr, base->length) == 0)
+            if (ia5ncasecmp(p, baseptr, base->length) == 0)
                 return X509_V_OK;
         }
         return X509_V_ERR_PERMITTED_VIOLATION;
     }
 
     if ((base->length != (int)hostlen)
-        || strncasecmp(hostptr, baseptr, hostlen))
+        || ia5ncasecmp(hostptr, baseptr, hostlen))
         return X509_V_ERR_PERMITTED_VIOLATION;
 
     return X509_V_OK;