From 2135a3bb15dcf97404b8279576ae63c6920f184d Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Wed, 27 Jul 2011 16:26:44 -0400
Subject: [PATCH] libipa_hbac: Support case-insensitive comparisons with UTF8

---
 Makefile.am                        |    3 +-
 configure.ac                       |    1 +
 contrib/sssd.spec.in               |    1 +
 src/external/libunistring.m4       |    9 +++
 src/providers/ipa/hbac_evaluator.c |  114 +++++++++++++++++++++++++++++++-----
 5 files changed, 111 insertions(+), 17 deletions(-)
 create mode 100644 src/external/libunistring.m4

diff --git a/Makefile.am b/Makefile.am
index 6829788ae77760e447da009f4747212d7914812c..6647528c82a1653c792940298060753bf65fa5d4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -378,7 +378,8 @@ dist_pkgconfig_DATA += src/providers/ipa/ipa_hbac.pc
 libipa_hbac_la_SOURCES = \
     src/providers/ipa/hbac_evaluator.c
 libipa_hbac_la_LDFLAGS = \
-    -version 0:0:0
+    -version 0:1:0 \
+    -lunistring
 
 include_HEADERS = \
     src/providers/ipa/ipa_hbac.h
diff --git a/configure.ac b/configure.ac
index 0e2e6d44a0cc4b33fda39b3eaf3dcd383d1e09dc..248a9ae3b96d49ae3c8cb1b7f4d98cccef2a5699 100644
--- a/configure.ac
+++ b/configure.ac
@@ -114,6 +114,7 @@ m4_include([src/external/libkeyutils.m4])
 m4_include([src/external/libnl.m4])
 m4_include([src/external/systemd.m4])
 m4_include([src/util/signal.m4])
+m4_include([src/external/libunistring.m4])
 
 WITH_INITSCRIPT
 if test x$initscript = xsystemd; then
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index 4e7db38a7746d91faaa9167b6a2666a28ddd29f1..8c592a5868af675042d6d4c2318c0b28464ef9b6 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -86,6 +86,7 @@ BuildRequires: libnl-devel
 BuildRequires: nscd
 BuildRequires: gettext-devel
 BuildRequires: pkgconfig
+BuildRequires: libunistring-devel
 
 %description
 Provides a set of daemons to manage access to remote directories and
diff --git a/src/external/libunistring.m4 b/src/external/libunistring.m4
new file mode 100644
index 0000000000000000000000000000000000000000..69c54fe3faf4bffea120cb30e1d3ed73a2a104c8
--- /dev/null
+++ b/src/external/libunistring.m4
@@ -0,0 +1,9 @@
+AC_CHECK_HEADERS(unistr.h,
+    [AC_CHECK_LIB([unistring], [u8_strlen], [ UNISTRING_LIBS="-lunistring" ], [AC_MSG_ERROR([No usable libunistring library found])])],
+    [AC_MSG_ERROR([libunistring header files are not installed])]
+)
+
+AC_CHECK_HEADERS(unicase.h,
+    [AC_CHECK_LIB([unistring], [u8_casecmp], [ UNISTRING_LIBS="-lunistring" ], [AC_MSG_ERROR([No usable libunistring library found])])],
+    [AC_MSG_ERROR([libunistring header files are not installed])]
+)
\ No newline at end of file
diff --git a/src/providers/ipa/hbac_evaluator.c b/src/providers/ipa/hbac_evaluator.c
index e120d51e43639081ec6c90da47d41cdc08efc8d0..ee39a09ae35cb392b024f009483a22e2b5320a38 100644
--- a/src/providers/ipa/hbac_evaluator.c
+++ b/src/providers/ipa/hbac_evaluator.c
@@ -25,8 +25,20 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <unistr.h>
+#include <unicase.h>
+#include <errno.h>
 #include "providers/ipa/ipa_hbac.h"
 
+#ifndef HAVE_ERRNO_T
+#define HAVE_ERRNO_T
+typedef int errno_t;
+#endif
+
+#ifndef EOK
+#define EOK 0
+#endif
+
 /* Placeholder structure for future HBAC time-based
  * evaluation rules
  */
@@ -102,13 +114,17 @@ done:
     return result;
 }
 
-static bool hbac_evaluate_element(struct hbac_rule_element *rule_el,
-                                  struct hbac_request_element *req_el);
+static errno_t hbac_evaluate_element(struct hbac_rule_element *rule_el,
+                                     struct hbac_request_element *req_el,
+                                     bool *matched);
 
 enum hbac_eval_result_int hbac_evaluate_rule(struct hbac_rule *rule,
                                              struct hbac_eval_req *hbac_req,
                                              enum hbac_error_code *error)
 {
+    errno_t ret;
+    bool matched;
+
     if (!rule->enabled) return HBAC_EVAL_UNMATCHED;
 
     /* Make sure we have all elements */
@@ -121,43 +137,90 @@ enum hbac_eval_result_int hbac_evaluate_rule(struct hbac_rule *rule,
     }
 
     /* Check users */
-    if (!hbac_evaluate_element(rule->users, hbac_req->user)) {
+    ret = hbac_evaluate_element(rule->users,
+                                hbac_req->user,
+                                &matched);
+    if (ret != EOK) {
+        *error = HBAC_ERROR_UNPARSEABLE_RULE;
+        return HBAC_EVAL_MATCH_ERROR;
+    } else if (!matched) {
         return HBAC_EVAL_UNMATCHED;
     }
 
     /* Check services */
-    if (!hbac_evaluate_element(rule->services, hbac_req->service)) {
+    ret = hbac_evaluate_element(rule->services,
+                                hbac_req->service,
+                                &matched);
+    if (ret != EOK) {
+        *error = HBAC_ERROR_UNPARSEABLE_RULE;
+        return HBAC_EVAL_MATCH_ERROR;
+    } else if (!matched) {
         return HBAC_EVAL_UNMATCHED;
     }
 
     /* Check target hosts */
-    if (!hbac_evaluate_element(rule->targethosts, hbac_req->targethost)) {
+    ret = hbac_evaluate_element(rule->targethosts,
+                                hbac_req->targethost,
+                                &matched);
+    if (ret != EOK) {
+        *error = HBAC_ERROR_UNPARSEABLE_RULE;
+        return HBAC_EVAL_MATCH_ERROR;
+    } else if (!matched) {
         return HBAC_EVAL_UNMATCHED;
     }
 
     /* Check source hosts */
-    if (!hbac_evaluate_element(rule->srchosts, hbac_req->srchost)) {
+    ret = hbac_evaluate_element(rule->srchosts,
+                                hbac_req->srchost,
+                                &matched);
+    if (ret != EOK) {
+        *error = HBAC_ERROR_UNPARSEABLE_RULE;
+        return HBAC_EVAL_MATCH_ERROR;
+    } else if (!matched) {
         return HBAC_EVAL_UNMATCHED;
     }
-
     return HBAC_EVAL_MATCHED;
 }
 
-static bool hbac_evaluate_element(struct hbac_rule_element *rule_el,
-                                  struct hbac_request_element *req_el)
+static errno_t hbac_evaluate_element(struct hbac_rule_element *rule_el,
+                                     struct hbac_request_element *req_el,
+                                     bool *matched)
 {
     size_t i, j;
+    const uint8_t *rule_name;
+    const uint8_t *req_name;
+    int result;
+    int ret;
 
     if (rule_el->category & HBAC_CATEGORY_ALL) {
-        return true;
+        *matched = true;
+        return EOK;
     }
 
     /* First check the name list */
     if (rule_el->names) {
         for (i = 0; rule_el->names[i]; i++) {
             if (req_el->name != NULL) {
-                if (strcmp(rule_el->names[i], req_el->name) == 0) {
-                    return true;
+                rule_name = (const uint8_t *) rule_el->names[i];
+                req_name = (const uint8_t *) req_el->name;
+
+                /* Do a case-insensitive comparison.
+                 * The input must be encoded in UTF8.
+                 * We have no way of knowing the language,
+                 * so we'll pass NULL for the language and
+                 * hope for the best.
+                 */
+                errno = 0;
+                ret = u8_casecmp(rule_name, u8_strlen(rule_name),
+                                 req_name, u8_strlen(req_name),
+                                 NULL, NULL, &result);
+                if (ret < 0) {
+                    return errno;
+                }
+
+                if (result == 0) {
+                    *matched = true;
+                    return EOK;
                 }
             }
         }
@@ -168,17 +231,36 @@ static bool hbac_evaluate_element(struct hbac_rule_element *rule_el,
          * Check for group membership
          */
         for (i = 0; rule_el->groups[i]; i++) {
+            rule_name = (const uint8_t *) rule_el->groups[i];
+
             for (j = 0; req_el->groups[j]; j++) {
-                if (strcmp(rule_el->groups[i],
-                           req_el->groups[j]) == 0) {
-                    return true;
+                req_name = (const uint8_t *) req_el->groups[j];
+
+                /* Do a case-insensitive comparison.
+                 * The input must be encoded in UTF8.
+                 * We have no way of knowing the language,
+                 * so we'll pass NULL for the language and
+                 * hope for the best.
+                 */
+                errno = 0;
+                ret = u8_casecmp(rule_name, u8_strlen(rule_name),
+                                 req_name, u8_strlen(req_name),
+                                 NULL, NULL, &result);
+                if (ret < 0) {
+                    return errno;
+                }
+
+                if (result == 0) {
+                    *matched = true;
+                    return EOK;
                 }
             }
         }
     }
 
     /* Not found in groups either */
-    return false;
+    *matched = false;
+    return EOK;
 }
 
 const char *hbac_result_string(enum hbac_eval_result result)
-- 
1.7.6

