URL: https://github.com/SSSD/sssd/pull/494
Author: sumit-bose
 Title: #494: Backport support for certificates in idoverrides to sssd-1.13
Action: synchronized

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/494/head:pr494
git checkout pr494
From 8349336ecb38fabef619eab5456e7971657cc2e0 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Mon, 30 Nov 2015 12:14:16 +0100
Subject: [PATCH 01/13] sysdb: add sysdb_attrs_add_base64_blob()

Reviewed-by: Jakub Hrozek <jhro...@redhat.com>
(cherry picked from commit 6cdeb0923c16e3fafe21aaadca6dac1d71474c31)
---
 src/db/sysdb.c                      | 22 ++++++++++++++++++++++
 src/db/sysdb.h                      |  2 ++
 src/tests/cmocka/test_sysdb_utils.c | 36 ++++++++++++++++++++++++++++++++++++
 3 files changed, 60 insertions(+)

diff --git a/src/db/sysdb.c b/src/db/sysdb.c
index d4366a3c7..8552d77b7 100644
--- a/src/db/sysdb.c
+++ b/src/db/sysdb.c
@@ -23,6 +23,7 @@
 #include "util/util.h"
 #include "util/strtonum.h"
 #include "util/sss_utf8.h"
+#include "util/crypto/sss_crypto.h"
 #include "db/sysdb_private.h"
 #include "confdb/confdb.h"
 #include <time.h>
@@ -634,6 +635,27 @@ int sysdb_attrs_add_mem(struct sysdb_attrs *attrs, const char *name,
 	return sysdb_attrs_add_val(attrs, name, &v);
 }
 
+int sysdb_attrs_add_base64_blob(struct sysdb_attrs *attrs, const char *name,
+                                const char *base64_str)
+{
+    struct ldb_val v;
+    int ret;
+
+    if (base64_str == NULL) {
+        return EINVAL;
+    }
+
+    v.data = sss_base64_decode(attrs, base64_str, &v.length);
+    if (v.data == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n");
+        return ENOMEM;
+    }
+
+    ret = sysdb_attrs_add_val(attrs, name, &v);
+    talloc_free(v.data);
+    return ret;
+}
+
 int sysdb_attrs_add_bool(struct sysdb_attrs *attrs,
                          const char *name, bool value)
 {
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 0413d2a3b..732ca14f8 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -323,6 +323,8 @@ int sysdb_attrs_add_lower_case_string(struct sysdb_attrs *attrs, bool safe,
                                       const char *name, const char *str);
 int sysdb_attrs_add_mem(struct sysdb_attrs *attrs, const char *name,
                         const void *mem, size_t size);
+int sysdb_attrs_add_base64_blob(struct sysdb_attrs *attrs, const char *name,
+                                const char *base64_str);
 int sysdb_attrs_add_bool(struct sysdb_attrs *attrs,
                          const char *name, bool value);
 int sysdb_attrs_add_long(struct sysdb_attrs *attrs,
diff --git a/src/tests/cmocka/test_sysdb_utils.c b/src/tests/cmocka/test_sysdb_utils.c
index b791f14b7..570fe0235 100644
--- a/src/tests/cmocka/test_sysdb_utils.c
+++ b/src/tests/cmocka/test_sysdb_utils.c
@@ -103,6 +103,41 @@ static void test_sysdb_handle_original_uuid(void **state)
     talloc_free(dest_attrs);
 }
 
+#define TEST_BASE64_ABC "YWJj"
+#define TEST_BASE64_123 "AQID"
+static void test_sysdb_attrs_add_base64_blob(void **state)
+{
+    struct sysdb_attrs *attrs;
+    struct ldb_message_element *el;
+    char zero[] = { '\1', '\2', '\3' };
+    int ret;
+
+    attrs = sysdb_new_attrs(NULL);
+    assert_non_null(attrs);
+
+    ret = sysdb_attrs_add_base64_blob(attrs, "testAttrABC", TEST_BASE64_ABC);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_attrs_add_base64_blob(attrs, "testAttr000", TEST_BASE64_123);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_attrs_get_el(attrs, "testAttrABC", &el);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(el->num_values, 1);
+    assert_non_null(el->values);
+    assert_non_null(el->values[0].data);
+    assert_int_equal(el->values[0].length, 3);
+    assert_memory_equal(el->values[0].data, "abc", 3);
+
+    ret = sysdb_attrs_get_el(attrs, "testAttr000", &el);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(el->num_values, 1);
+    assert_non_null(el->values);
+    assert_non_null(el->values[0].data);
+    assert_int_equal(el->values[0].length, 3);
+    assert_memory_equal(el->values[0].data, zero, 3);
+}
+
 int main(int argc, const char *argv[])
 {
     int rv;
@@ -116,6 +151,7 @@ int main(int argc, const char *argv[])
 
     const struct CMUnitTest tests[] = {
         cmocka_unit_test(test_sysdb_handle_original_uuid),
+        cmocka_unit_test(test_sysdb_attrs_add_base64_blob),
     };
 
     /* Set debug level to invalid value so we can deside if -d 0 was used. */

From 4fa0f11a3d9f031ac920e1b6e50ce41f2c3a02cc Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Wed, 6 Apr 2016 11:12:30 +0200
Subject: [PATCH 02/13] sysdb: add searches by certificate with overrides

Reviewed-by: Jakub Hrozek <jhro...@redhat.com>
(cherry picked from commit 6cb34580ee6e9e2c9190b77b10db8a3c43e3c9c8)
---
 src/db/sysdb.h        |  12 ++++++
 src/db/sysdb_search.c |  67 +++++++++++++++++++++++++++++++++
 src/db/sysdb_views.c  | 102 +++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 180 insertions(+), 1 deletion(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 732ca14f8..514a91323 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -546,6 +546,13 @@ errno_t sysdb_search_group_override_by_gid(TALLOC_CTX *mem_ctx,
                                             struct ldb_result **override_obj,
                                             struct ldb_result **orig_obj);
 
+errno_t sysdb_search_override_by_cert(TALLOC_CTX *mem_ctx,
+                                      struct sss_domain_info *domain,
+                                      const char *cert,
+                                      const char **attrs,
+                                      struct ldb_result **override_obj,
+                                      struct ldb_result **orig_obj);
+
 errno_t sysdb_add_overrides_to_object(struct sss_domain_info *domain,
                                       struct ldb_message *obj,
                                       struct ldb_message *override_obj,
@@ -724,6 +731,11 @@ int sysdb_get_user_attr_with_views(TALLOC_CTX *mem_ctx,
                                    const char **attributes,
                                    struct ldb_result **res);
 
+int sysdb_search_user_by_cert_with_views(TALLOC_CTX *mem_ctx,
+                                         struct sss_domain_info *domain,
+                                         const char *cert,
+                                         struct ldb_result **res);
+
 int sysdb_get_netgroup_attr(TALLOC_CTX *mem_ctx,
                             struct sss_domain_info *domain,
                             const char *netgrname,
diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
index 484af390c..8fd59ccee 100644
--- a/src/db/sysdb_search.c
+++ b/src/db/sysdb_search.c
@@ -1858,3 +1858,70 @@ errno_t sysdb_get_real_name(TALLOC_CTX *mem_ctx,
     talloc_free(tmp_ctx);
     return ret;
 }
+
+int sysdb_search_user_by_cert_with_views(TALLOC_CTX *mem_ctx,
+                                         struct sss_domain_info *domain,
+                                         const char *cert,
+                                         struct ldb_result **res)
+{
+    TALLOC_CTX *tmp_ctx;
+    int ret;
+    struct ldb_result *orig_obj = NULL;
+    struct ldb_result *override_obj = NULL;
+    const char *attrs[] = SYSDB_PW_ATTRS;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    /* If there are views we first have to search the overrides for matches */
+    if (DOM_HAS_VIEWS(domain)) {
+        ret = sysdb_search_override_by_cert(tmp_ctx, domain, cert, attrs,
+                                            &override_obj, &orig_obj);
+        if (ret != EOK && ret != ENOENT) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "sysdb_search_override_by_cert failed.\n");
+            goto done;
+        }
+    }
+
+    /* If there are no views or nothing was found in the overrides the
+     * original objects are searched. */
+    if (orig_obj == NULL) {
+        ret = sysdb_search_user_by_cert(tmp_ctx, domain, cert, &orig_obj);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_cert failed.\n");
+            goto done;
+        }
+    }
+
+    /* If there are views we have to check if override values must be added to
+     * the original object. */
+    if (DOM_HAS_VIEWS(domain) && orig_obj->count == 1) {
+        ret = sysdb_add_overrides_to_object(domain, orig_obj->msgs[0],
+                          override_obj == NULL ? NULL : override_obj->msgs[0],
+                          NULL);
+        if (ret == ENOENT) {
+            *res = talloc_zero(mem_ctx, struct ldb_result);
+            if (*res == NULL) {
+                DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+                ret = ENOMEM;
+            } else {
+                ret = EOK;
+            }
+            goto done;
+        } else if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_add_overrides_to_object failed.\n");
+            goto done;
+        }
+    }
+
+    *res = talloc_steal(mem_ctx, orig_obj);
+    ret = EOK;
+
+done:
+
+    talloc_free(tmp_ctx);
+    return ret;
+}
diff --git a/src/db/sysdb_views.c b/src/db/sysdb_views.c
index cc1bc85f9..e19b4457e 100644
--- a/src/db/sysdb_views.c
+++ b/src/db/sysdb_views.c
@@ -20,6 +20,7 @@
 */
 
 #include "util/util.h"
+#include "util/cert.h"
 #include "db/sysdb_private.h"
 
 /* In general is should not be possible that there is a view container without
@@ -746,6 +747,7 @@ errno_t sysdb_apply_default_override(struct sss_domain_info *domain,
                                     SYSDB_SHELL,
                                     SYSDB_NAME,
                                     SYSDB_SSH_PUBKEY,
+                                    SYSDB_USER_CERT,
                                     NULL };
     bool override_attrs_found = false;
 
@@ -791,8 +793,10 @@ errno_t sysdb_apply_default_override(struct sss_domain_info *domain,
                 }
             } else {
                 num_values = el->num_values;
-                /* Only SYSDB_SSH_PUBKEY is allowed to have multiple values. */
+                /* Only SYSDB_SSH_PUBKEY and SYSDB_USER_CERT are allowed to
+                 * have multiple values. */
                 if (strcmp(allowed_attrs[c], SYSDB_SSH_PUBKEY) != 0
+                        && strcmp(allowed_attrs[c], SYSDB_USER_CERT) != 0
                         && num_values != 1) {
                     DEBUG(SSSDBG_MINOR_FAILURE,
                           "Override attribute for [%s] has more [%zd] " \
@@ -845,6 +849,7 @@ errno_t sysdb_apply_default_override(struct sss_domain_info *domain,
 
 #define SYSDB_USER_NAME_OVERRIDE_FILTER "(&(objectClass="SYSDB_OVERRIDE_USER_CLASS")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
 #define SYSDB_USER_UID_OVERRIDE_FILTER "(&(objectClass="SYSDB_OVERRIDE_USER_CLASS")("SYSDB_UIDNUM"=%lu))"
+#define SYSDB_USER_CERT_OVERIDE_FILTER "(&(objectClass="SYSDB_OVERRIDE_USER_CLASS")%s)"
 #define SYSDB_GROUP_NAME_OVERRIDE_FILTER "(&(objectClass="SYSDB_OVERRIDE_GROUP_CLASS")(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))"
 #define SYSDB_GROUP_GID_OVERRIDE_FILTER "(&(objectClass="SYSDB_OVERRIDE_GROUP_CLASS")("SYSDB_GIDNUM"=%lu))"
 
@@ -854,6 +859,100 @@ enum override_object_type {
     OO_TYPE_GROUP
 };
 
+errno_t sysdb_search_override_by_cert(TALLOC_CTX *mem_ctx,
+                                      struct sss_domain_info *domain,
+                                      const char *cert,
+                                      const char **attrs,
+                                      struct ldb_result **override_obj,
+                                      struct ldb_result **orig_obj)
+{
+    TALLOC_CTX *tmp_ctx;
+    struct ldb_dn *base_dn;
+    struct ldb_result *override_res;
+    struct ldb_result *orig_res;
+    char *cert_filter;
+    int ret;
+    const char *orig_obj_dn;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    base_dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+                             SYSDB_TMPL_VIEW_SEARCH_BASE, domain->view_name);
+    if (base_dn == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new_fmt failed.\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sss_cert_derb64_to_ldap_filter(tmp_ctx, cert, SYSDB_USER_CERT,
+                                         &cert_filter);
+
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "sss_cert_derb64_to_ldap_filter failed.\n");
+        goto done;
+    }
+
+    ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &override_res, base_dn,
+                     LDB_SCOPE_SUBTREE, attrs, SYSDB_USER_CERT_OVERIDE_FILTER,
+                     cert_filter);
+    if (ret != LDB_SUCCESS) {
+        ret = sysdb_error_to_errno(ret);
+        goto done;
+    }
+
+    if (override_res->count == 0) {
+        DEBUG(SSSDBG_TRACE_FUNC, "No user override found for cert [%s].\n",
+                                 cert);
+        ret = ENOENT;
+        goto done;
+    } else if (override_res->count > 1) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Found more than one override for cert [%s].\n", cert);
+        ret = EINVAL;
+        goto done;
+    }
+
+    if (orig_obj != NULL) {
+        orig_obj_dn = ldb_msg_find_attr_as_string(override_res->msgs[0],
+                                                  SYSDB_OVERRIDE_OBJECT_DN,
+                                                  NULL);
+        if (orig_obj_dn == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Missing link to original object in override [%s].\n",
+                  ldb_dn_get_linearized(override_res->msgs[0]->dn));
+            ret = EINVAL;
+            goto done;
+        }
+
+        base_dn = ldb_dn_new(tmp_ctx, domain->sysdb->ldb, orig_obj_dn);
+        if (base_dn == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
+            ret = ENOMEM;
+            goto done;
+        }
+
+        ret = ldb_search(domain->sysdb->ldb, tmp_ctx, &orig_res, base_dn,
+                         LDB_SCOPE_BASE, attrs, NULL);
+        if (ret != LDB_SUCCESS) {
+            ret = sysdb_error_to_errno(ret);
+            goto done;
+        }
+
+        *orig_obj = talloc_steal(mem_ctx, orig_res);
+    }
+
+    *override_obj = talloc_steal(mem_ctx, override_res);
+
+    ret = EOK;
+
+done:
+    talloc_zfree(tmp_ctx);
+    return ret;
+}
+
 static errno_t sysdb_search_override_by_name(TALLOC_CTX *mem_ctx,
                                              struct sss_domain_info *domain,
                                              const char *name,
@@ -1180,6 +1279,7 @@ errno_t sysdb_add_overrides_to_object(struct sss_domain_info *domain,
         {SYSDB_SHELL, OVERRIDE_PREFIX SYSDB_SHELL},
         {SYSDB_NAME, OVERRIDE_PREFIX SYSDB_NAME},
         {SYSDB_SSH_PUBKEY, OVERRIDE_PREFIX SYSDB_SSH_PUBKEY},
+        {SYSDB_USER_CERT, OVERRIDE_PREFIX SYSDB_USER_CERT},
         {NULL, NULL}
     };
     size_t c;

From b690c940b007e0dcb18dbe77d8224b2589074c12 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Wed, 6 Apr 2016 11:13:22 +0200
Subject: [PATCH 03/13] cache_req: use overide aware call for lookup by
 certificate

Reviewed-by: Jakub Hrozek <jhro...@redhat.com>
(cherry picked from commit cdc3e9dc42e13f01d8e2623e92dd046a5bb169f1 with
backport fixes)
---
 src/responder/common/responder_cache_req.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/responder/common/responder_cache_req.c b/src/responder/common/responder_cache_req.c
index 516b00efc..986310442 100644
--- a/src/responder/common/responder_cache_req.c
+++ b/src/responder/common/responder_cache_req.c
@@ -520,8 +520,8 @@ static errno_t cache_req_get_object(TALLOC_CTX *mem_ctx,
         break;
     case CACHE_REQ_USER_BY_CERT:
         one_item_only = true;
-        ret = sysdb_search_user_by_cert(mem_ctx, input->domain,
-                                        input->cert, &result);
+        ret = sysdb_search_user_by_cert_with_views(mem_ctx, input->domain,
+                                                   input->cert, &result);
         break;
     case CACHE_REQ_USER_BY_FILTER:
         one_item_only = false;

From 9c98970c99614bb2bb91a3297e08d05b3d22a856 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Wed, 6 Apr 2016 11:15:32 +0200
Subject: [PATCH 04/13] ipa: add support for certificate overrides

Reviewed-by: Jakub Hrozek <jhro...@redhat.com>
(cherry picked from commit a1210c8db81a1cc0b45eb62a8450abcdea3afc7b)
---
 src/providers/ipa/ipa_common.h        |  1 +
 src/providers/ipa/ipa_opts.c          |  1 +
 src/providers/ipa/ipa_subdomains_id.c |  6 ++++++
 src/providers/ipa/ipa_views.c         | 25 +++++++++++++++++++++++++
 4 files changed, 33 insertions(+)

diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index d1688bb6a..51de819c8 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -129,6 +129,7 @@ enum ipa_override_attrs {
     IPA_AT_OVERRIDE_GROUP_NAME,
     IPA_AT_OVERRIDE_GROUP_GID_NUMBER,
     IPA_AT_OVERRIDE_USER_SSH_PUBLIC_KEY,
+    IPA_AT_OVERRIDE_USER_CERT,
 
     IPA_OPTS_OVERRIDE
 };
diff --git a/src/providers/ipa/ipa_opts.c b/src/providers/ipa/ipa_opts.c
index 6000e61ab..59779955f 100644
--- a/src/providers/ipa/ipa_opts.c
+++ b/src/providers/ipa/ipa_opts.c
@@ -290,6 +290,7 @@ struct sdap_attr_map ipa_override_map[] = {
     { "ldap_group_name", "cn", SYSDB_NAME, NULL },
     { "ldap_group_gid_number", "gidNumber", SYSDB_GIDNUM, NULL },
     { "ldap_user_ssh_public_key", "ipaSshPubKey", SYSDB_SSH_PUBKEY, NULL },
+    { "ldap_user_certificate", "userCertificate;binary", SYSDB_USER_CERT, NULL },
     SDAP_ATTR_MAP_TERMINATOR
 };
 
diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
index 0956046d7..f7a01c28e 100644
--- a/src/providers/ipa/ipa_subdomains_id.c
+++ b/src/providers/ipa/ipa_subdomains_id.c
@@ -397,6 +397,7 @@ struct tevent_req *ipa_get_subdom_acct_send(TALLOC_CTX *memctx,
         case BE_REQ_USER:
         case BE_REQ_GROUP:
         case BE_REQ_BY_SECID:
+        case BE_REQ_BY_CERT:
         case BE_REQ_USER_AND_GROUP:
         case BE_REQ_INITGROUPS:
             ret = EOK;
@@ -490,6 +491,11 @@ static void ipa_get_subdom_acct_connected(struct tevent_req *subreq)
                 return;
             }
             break;
+        case BE_FILTER_CERT:
+            DEBUG(SSSDBG_OP_FAILURE, "Lookup by certificate not supported yet.\n");
+            state->dp_error = dp_error;
+            tevent_req_error(req, EINVAL);
+            return;
         default:
             DEBUG(SSSDBG_OP_FAILURE, "Invalid sub-domain filter type.\n");
             state->dp_error = dp_error;
diff --git a/src/providers/ipa/ipa_views.c b/src/providers/ipa/ipa_views.c
index 00dcbeb75..76528a60c 100644
--- a/src/providers/ipa/ipa_views.c
+++ b/src/providers/ipa/ipa_views.c
@@ -24,6 +24,7 @@
 
 #include "util/util.h"
 #include "util/strtonum.h"
+#include "util/cert.h"
 #include "providers/ldap/sdap_async.h"
 #include "providers/ipa/ipa_id.h"
 
@@ -35,6 +36,8 @@ static errno_t be_acct_req_to_override_filter(TALLOC_CTX *mem_ctx,
     char *filter;
     uint32_t id;
     char *endptr;
+    char *cert_filter;
+    int ret;
 
     switch (ar->filter_type) {
     case BE_FILTER_NAME:
@@ -140,6 +143,28 @@ static errno_t be_acct_req_to_override_filter(TALLOC_CTX *mem_ctx,
         }
         break;
 
+    case BE_FILTER_CERT:
+        if ((ar->entry_type & BE_REQ_TYPE_MASK) == BE_REQ_BY_CERT) {
+            ret = sss_cert_derb64_to_ldap_filter(mem_ctx, ar->filter_value,
+                         ipa_opts->override_map[IPA_AT_OVERRIDE_USER_CERT].name,
+                         &cert_filter);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE,
+                      "sss_cert_derb64_to_ldap_filter failed.\n");
+                return ret;
+            }
+            filter = talloc_asprintf(mem_ctx, "(&(objectClass=%s)%s)",
+                        ipa_opts->override_map[IPA_OC_OVERRIDE_USER].name,
+                        cert_filter);
+            talloc_free(cert_filter);
+        } else {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Unexpected entry type [%d] for certificate filter.\n",
+                  ar->entry_type);
+            return EINVAL;
+        }
+        break;
+
     default:
         DEBUG(SSSDBG_OP_FAILURE, "Invalid sub-domain filter type.\n");
         return EINVAL;

From 701e276bf7c4edec0b583c2894b12131952e88db Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Wed, 6 Apr 2016 11:18:38 +0200
Subject: [PATCH 05/13] nss: include certificates in full result list

Reviewed-by: Jakub Hrozek <jhro...@redhat.com>
(cherry picked from commit dc936929c01647c0fc116a112cee200156328037)
---
 src/responder/nss/nsssrv_cmd.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 61e961efc..a6f4ca90f 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -4510,6 +4510,7 @@ static errno_t nss_cmd_getsidby_search(struct nss_dom_ctx *dctx)
                                    SYSDB_AD_ACCOUNT_EXPIRES,
                                    SYSDB_AD_USER_ACCOUNT_CONTROL,
                                    SYSDB_SSH_PUBKEY,
+                                   SYSDB_USER_CERT,
                                    SYSDB_ORIG_DN,
                                    SYSDB_ORIG_MEMBEROF,
                                    SYSDB_DEFAULT_ATTRS, NULL};
@@ -5079,6 +5080,7 @@ static errno_t fill_orig(struct sss_packet *packet,
                                     SYSDB_AD_ACCOUNT_EXPIRES,
                                     SYSDB_AD_USER_ACCOUNT_CONTROL,
                                     SYSDB_SSH_PUBKEY,
+                                    SYSDB_USER_CERT,
                                     SYSDB_ORIG_DN,
                                     SYSDB_ORIG_MEMBEROF,
                                     NULL};

From f2de2abd8e7defc74ff4b44b7615e8411d7f8df8 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Wed, 6 Apr 2016 18:07:37 +0200
Subject: [PATCH 06/13] ipa: save cert as blob in the cache

The IPA extdom plugin returns the user certificate base64 encoded.
Before the IPA client can store it in the cache it must be decoded so
that it is stored as a binary as the certificate from other sources.

Reviewed-by: Jakub Hrozek <jhro...@redhat.com>
(cherry picked from commit cf89f552f06b95bd69d8c61aaa55a330a5d9f6e6 with
backport fixes)
---
 src/providers/ipa/ipa_s2n_exop.c | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
index 1d233cd52..676b55f37 100644
--- a/src/providers/ipa/ipa_s2n_exop.c
+++ b/src/providers/ipa/ipa_s2n_exop.c
@@ -22,6 +22,8 @@
 #include "util/util.h"
 #include "util/sss_nss.h"
 #include "db/sysdb.h"
+#include "util/strtonum.h"
+#include "util/crypto/sss_crypto.h"
 #include "providers/ldap/sdap_async_private.h"
 #include "providers/ldap/ldap_common.h"
 #include "providers/ipa/ipa_id.h"
@@ -493,8 +495,22 @@ static errno_t get_extra_attrs(BerElement *ber, struct resp_attrs *resp_attrs)
 
         for (c = 0; values[c] != NULL; c++) {
 
-            v.data = (uint8_t *) values[c]->bv_val;
-            v.length = values[c]->bv_len;
+            if (strcmp(name, SYSDB_USER_CERT) == 0) {
+                if (values[c]->bv_val[values[c]->bv_len] != '\0') {
+                    DEBUG(SSSDBG_OP_FAILURE,
+                          "base64 encoded certificate not 0-terminated.\n");
+                    return EINVAL;
+                }
+
+                v.data = sss_base64_decode(NULL, values[c]->bv_val, &v.length);
+                if (v.data == NULL) {
+                    DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n");
+                    return EINVAL;
+                }
+            } else {
+                v.data = (uint8_t *)values[c]->bv_val;
+                v.length = values[c]->bv_len;
+            }
 
             ret = sysdb_attrs_add_val(resp_attrs->sysdb_attrs, name, &v);
             if (ret != EOK) {

From 34be457e0af9df932531cb2d8203e763a2c09399 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Wed, 6 Apr 2016 18:10:48 +0200
Subject: [PATCH 07/13] AD: read user certificate if available

Reviewed-by: Jakub Hrozek <jhro...@redhat.com>
(cherry picked from commit ffe2522a208cddd415d7c3498dcc73ffda863b6f)
---
 src/providers/ad/ad_opts.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/providers/ad/ad_opts.c b/src/providers/ad/ad_opts.c
index c77118d5a..3567843e6 100644
--- a/src/providers/ad/ad_opts.c
+++ b/src/providers/ad/ad_opts.c
@@ -219,7 +219,7 @@ struct sdap_attr_map ad_2008r2_user_map[] = {
     { "ldap_user_nds_login_allowed_time_map", NULL, SYSDB_NDS_LOGIN_ALLOWED_TIME_MAP, NULL },
     { "ldap_user_ssh_public_key", NULL, SYSDB_SSH_PUBKEY, NULL },
     { "ldap_user_auth_type", NULL, SYSDB_AUTH_TYPE, NULL },
-    { "ldap_user_certificate", NULL, SYSDB_USER_CERT, NULL },
+    { "ldap_user_certificate", "userCertificate;binary", SYSDB_USER_CERT, NULL },
     SDAP_ATTR_MAP_TERMINATOR
 };
 

From 44924d4b6d21d5fe969ca2ca25b3b4d78b0b2749 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Wed, 6 Apr 2016 18:12:31 +0200
Subject: [PATCH 08/13] nss: return user certificate base64 encoded

Reviewed-by: Jakub Hrozek <jhro...@redhat.com>
(cherry picked from commit 8822520e6552bbf5ad1b62a4f88dd31a9c8475f1)
---
 src/responder/nss/nsssrv_cmd.c | 25 ++++++++++++++++++++-----
 1 file changed, 20 insertions(+), 5 deletions(-)

diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index a6f4ca90f..0f4ce3632 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -22,6 +22,7 @@
 #include "util/util.h"
 #include "util/sss_nss.h"
 #include "util/sss_cli_cmd.h"
+#include "util/crypto/sss_crypto.h"
 #include "responder/nss/nsssrv.h"
 #include "responder/nss/nsssrv_private.h"
 #include "responder/nss/nsssrv_netgroup.h"
@@ -5007,8 +5008,9 @@ static errno_t process_attr_list(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
     size_t d;
     struct sized_string *keys;
     struct sized_string *vals;
-    struct ldb_val *val;
+    struct ldb_val val;
     struct ldb_message_element *el;
+    bool use_base64;
 
     keys = *_keys;
     vals = *_vals;
@@ -5027,18 +5029,31 @@ static errno_t process_attr_list(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
                     return ENOMEM;
                 }
             }
+
+            use_base64 = false;
+            if (strcmp(attr_list[c], SYSDB_USER_CERT) == 0) {
+                use_base64 = true;
+            }
             for (d = 0; d < el->num_values; d++) {
                 to_sized_string(&keys[*found], attr_list[c]);
                 *sum += keys[*found].len;
-                val = &(el->values[d]);
-                if (val == NULL || val->data == NULL
-                        || val->data[val->length] != '\0') {
+                if (use_base64) {
+                    val.data = (uint8_t *) sss_base64_encode(vals,
+                                                          el->values[d].data,
+                                                          el->values[d].length);
+                    if (val.data != NULL) {
+                        val.length = strlen((char *)val.data);
+                    }
+                } else {
+                    val = el->values[d];
+                }
+                if (val.data == NULL || val.data[val.length] != '\0') {
                     DEBUG(SSSDBG_CRIT_FAILURE,
                           "Unexpected attribute value found for [%s].\n",
                           attr_list[c]);
                     return EINVAL;
                 }
-                to_sized_string(&vals[*found], (const char *)val->data);
+                to_sized_string(&vals[*found], (const char *)val.data);
                 *sum += vals[*found].len;
 
                 (*found)++;

From 5b01e1d17fc0455a605b64d7cc9f0cb1a3dfa52a Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Fri, 8 Apr 2016 13:22:24 +0200
Subject: [PATCH 09/13] sss_override: add certificate support

Reviewed-by: Jakub Hrozek <jhro...@redhat.com>
(cherry picked from commit 2f90ec2e16f0c14c789d9ed20e008e3103337210)
---
 src/db/sysdb.h                             |  1 +
 src/man/sss_override.8.xml                 |  6 +++--
 src/tests/intg/ldap_local_override_test.py |  8 +++----
 src/tools/sss_override.c                   | 38 ++++++++++++++++++++++++++----
 4 files changed, 42 insertions(+), 11 deletions(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 514a91323..8f844cde4 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -214,6 +214,7 @@
                         SYSDB_PRIMARY_GROUP_GIDNUM, \
                         SYSDB_SID_STR, \
                         SYSDB_UPN, \
+                        SYSDB_USER_CERT, \
                         SYSDB_OVERRIDE_DN, \
                         SYSDB_OVERRIDE_OBJECT_DN, \
                         SYSDB_DEFAULT_OVERRIDE_NAME, \
diff --git a/src/man/sss_override.8.xml b/src/man/sss_override.8.xml
index 1e741e741..9f4d1c81d 100644
--- a/src/man/sss_override.8.xml
+++ b/src/man/sss_override.8.xml
@@ -64,6 +64,8 @@
                     <optional><option>-h,--home</option> HOME</optional>
                     <optional><option>-s,--shell</option> SHELL</optional>
                     <optional><option>-c,--gecos</option> GECOS</optional>
+                    <optional><option>-x,--certificate</option>
+                                     BASE64 ENCODED CERTIFICATE</optional>
                 </term>
                 <listitem>
                     <para>
@@ -123,7 +125,7 @@
                         The format is:
                     </para>
                     <para>
-                        original_name:name:uid:gid:gecos:home:shell
+                        original_name:name:uid:gid:gecos:home:shell:base64_encoded_certificate
                     </para>
                     <para>
                         where original_name is original name of the user whose
@@ -138,7 +140,7 @@
                         ckent:superman::::::
                     </para>
                     <para>
-                        ck...@krypton.com::501:501:Superman:/home/earth:/bin/bash
+                        ck...@krypton.com::501:501:Superman:/home/earth:/bin/bash:
                     </para>
                 </listitem>
             </varlistentry>
diff --git a/src/tests/intg/ldap_local_override_test.py b/src/tests/intg/ldap_local_override_test.py
index 7612170ec..771ca0574 100644
--- a/src/tests/intg/ldap_local_override_test.py
+++ b/src/tests/intg/ldap_local_override_test.py
@@ -588,11 +588,11 @@ def test_show_user_override(ldap_conn, env_show_user_override):
 
     out = check_output(['sss_override', 'user-show', 'user1'])
     assert out == "user1@LDAP:ov_user1:10010:20010:Overriden User 1:"\
-                  "/home/ov/user1:/bin/ov_user1_shell\n"
+                  "/home/ov/user1:/bin/ov_user1_shell:\n"
 
     out = check_output(['sss_override', 'user-show', 'user2@LDAP'])
     assert out == "user2@LDAP:ov_user2:10020:20020:Overriden User 2:"\
-                  "/home/ov/user2:/bin/ov_user2_shell\n"
+                  "/home/ov/user2:/bin/ov_user2_shell:\n"
 
     # Return error on non-existing user
     ret = subprocess.call(['sss_override', 'user-show', 'nonexisting_user'])
@@ -616,9 +616,9 @@ def test_find_user_override(ldap_conn, env_find_user_override):
 
     # Expected override of users
     exp_usr_ovrd = ['user1@LDAP:ov_user1:10010:20010:Overriden User 1:'
-                    '/home/ov/user1:/bin/ov_user1_shell',
+                    '/home/ov/user1:/bin/ov_user1_shell:',
                     'user2@LDAP:ov_user2:10020:20020:Overriden User 2:'
-                    '/home/ov/user2:/bin/ov_user2_shell']
+                    '/home/ov/user2:/bin/ov_user2_shell:']
 
     assert set(out.splitlines()) == set(exp_usr_ovrd)
 
diff --git a/src/tools/sss_override.c b/src/tools/sss_override.c
index a9e6d2b6c..482db6286 100644
--- a/src/tools/sss_override.c
+++ b/src/tools/sss_override.c
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 
 #include "util/util.h"
+#include "util/crypto/sss_crypto.h"
 #include "db/sysdb.h"
 #include "tools/common/sss_tools.h"
 #include "tools/common/sss_colondb.h"
@@ -39,6 +40,7 @@ struct override_user {
     const char *home;
     const char *shell;
     const char *gecos;
+    const char *cert;
 };
 
 struct override_group {
@@ -97,6 +99,7 @@ static int parse_cmdline_user_add(struct sss_cmdline *cmdline,
         {"home", 'h', POPT_ARG_STRING, &user->home, 0, _("Override home directory"), NULL },
         {"shell", 's', POPT_ARG_STRING, &user->shell, 0, _("Override shell"), NULL },
         {"gecos", 'c', POPT_ARG_STRING, &user->gecos, 0, _("Override gecos"), NULL },
+        {"certificate", 'x', POPT_ARG_STRING, &user->cert, 0, _("Override certificate"), NULL },
         POPT_TABLEEND
     };
 
@@ -298,7 +301,8 @@ static struct sysdb_attrs *build_attrs(TALLOC_CTX *mem_ctx,
                                        gid_t gid,
                                        const char *home,
                                        const char *shell,
-                                       const char *gecos)
+                                       const char *gecos,
+                                       const char *cert)
 {
     struct sysdb_attrs *attrs;
     errno_t ret;
@@ -350,6 +354,13 @@ static struct sysdb_attrs *build_attrs(TALLOC_CTX *mem_ctx,
         }
     }
 
+    if (cert != NULL) {
+        ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, cert);
+        if (ret != EOK) {
+            goto done;
+        }
+    }
+
     ret = EOK;
 
 done:
@@ -365,13 +376,13 @@ static struct sysdb_attrs *build_user_attrs(TALLOC_CTX *mem_ctx,
                                             struct override_user *user)
 {
     return build_attrs(mem_ctx, user->name, user->uid, user->gid, user->home,
-                       user->shell, user->gecos);
+                       user->shell, user->gecos, user->cert);
 }
 
 static struct sysdb_attrs *build_group_attrs(TALLOC_CTX *mem_ctx,
                                              struct override_group *group)
 {
-    return build_attrs(mem_ctx, group->name, 0, group->gid, 0, NULL, NULL);
+    return build_attrs(mem_ctx, group->name, 0, group->gid, 0, NULL, NULL, NULL);
 }
 
 static char *get_fqname(TALLOC_CTX *mem_ctx,
@@ -1103,6 +1114,7 @@ list_user_overrides(TALLOC_CTX *mem_ctx,
     size_t i;
     errno_t ret;
     const char *attrs[] = SYSDB_PW_ATTRS;
+    struct ldb_message_element *el;
 
     tmp_ctx = talloc_new(NULL);
     if (tmp_ctx == NULL) {
@@ -1138,6 +1150,20 @@ list_user_overrides(TALLOC_CTX *mem_ctx,
         objs[i].shell = ldb_msg_find_attr_as_string(msgs[i], SYSDB_SHELL, NULL);
         objs[i].gecos = ldb_msg_find_attr_as_string(msgs[i], SYSDB_GECOS, NULL);
 
+        el = ldb_msg_find_element(msgs[i], SYSDB_USER_CERT);
+        if (el != NULL && el->num_values > 0) {
+            /* Currently we support only 1 certificate override */
+            objs[i].cert = sss_base64_encode(objs, el->values[0].data,
+                                             el->values[0].length);
+            if (objs[i].cert == NULL) {
+                DEBUG(SSSDBG_CRIT_FAILURE, "sss_base64_encode failed.\n");
+                ret = ERR_INTERNAL;
+                goto done;
+            }
+        } else {
+            objs[i].cert = NULL;
+        }
+
         talloc_steal(objs, objs[i].orig_name);
         talloc_steal(objs, objs[i].name);
         talloc_steal(objs, objs[i].home);
@@ -1251,7 +1277,7 @@ static errno_t user_export(const char *filename,
 
         for (i = 0; objs[i].orig_name != NULL; i++) {
             /**
-             * Format: orig_name:name:uid:gid:gecos:home:shell
+             * Format: orig_name:name:uid:gid:gecos:home:shell:certificate
              */
             struct sss_colondb_write_field table[] = {
                 {SSS_COLONDB_STRING, {.str = objs[i].orig_name}},
@@ -1261,6 +1287,7 @@ static errno_t user_export(const char *filename,
                 {SSS_COLONDB_STRING, {.str = objs[i].gecos}},
                 {SSS_COLONDB_STRING, {.str = objs[i].home}},
                 {SSS_COLONDB_STRING, {.str = objs[i].shell}},
+                {SSS_COLONDB_STRING, {.str = objs[i].cert}},
                 {SSS_COLONDB_SENTINEL, {0}}
             };
 
@@ -1525,7 +1552,7 @@ static int override_user_import(struct sss_cmdline *cmdline,
     }
 
     /**
-     * Format: orig_name:name:uid:gid:gecos:home:shell
+     * Format: orig_name:name:uid:gid:gecos:home:shell:certificate
      */
     struct sss_colondb_read_field table[] = {
         {SSS_COLONDB_STRING, {.str = &obj.input_name}},
@@ -1535,6 +1562,7 @@ static int override_user_import(struct sss_cmdline *cmdline,
         {SSS_COLONDB_STRING, {.str = &obj.gecos}},
         {SSS_COLONDB_STRING, {.str = &obj.home}},
         {SSS_COLONDB_STRING, {.str = &obj.shell}},
+        {SSS_COLONDB_STRING, {.str = &obj.cert}},
         {SSS_COLONDB_SENTINEL, {0}}
     };
 

From 6c80f8a19ee65071c7bc8be46b8d5670fc8de8d9 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Fri, 22 Apr 2016 17:56:05 +0200
Subject: [PATCH 10/13] IPA: allow lookups by cert in sub-domains on the client

Reviewed-by: Jakub Hrozek <jhro...@redhat.com>
(cherry picked from commit 21513e51a4a2eb08f245333bf8f223713a3d7cb3)
---
 src/providers/ipa/ipa_s2n_exop.c      | 25 +++++++++++++++++++++----
 src/providers/ipa/ipa_subdomains.h    |  4 +++-
 src/providers/ipa/ipa_subdomains_id.c | 21 +++++++++++++++++----
 3 files changed, 41 insertions(+), 9 deletions(-)

diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
index 676b55f37..22e99cbef 100644
--- a/src/providers/ipa/ipa_s2n_exop.c
+++ b/src/providers/ipa/ipa_s2n_exop.c
@@ -33,7 +33,8 @@ enum input_types {
     INP_SID = 1,
     INP_NAME,
     INP_POSIX_UID,
-    INP_POSIX_GID
+    INP_POSIX_GID,
+    INP_CERT
 };
 
 enum request_types {
@@ -351,11 +352,22 @@ static errno_t s2n_encode_request(TALLOC_CTX *mem_ctx,
             break;
         case BE_REQ_BY_SECID:
             if (req_input->type == REQ_INP_SECID) {
-            ret = ber_printf(ber, "{ees}", INP_SID, request_type,
-                                           req_input->inp.secid);
+                ret = ber_printf(ber, "{ees}", INP_SID, request_type,
+                                               req_input->inp.secid);
             } else {
                 DEBUG(SSSDBG_OP_FAILURE, "Unexpected input type [%d].\n",
-                                          req_input->type == REQ_INP_ID);
+                                         req_input->type == REQ_INP_ID);
+                ret = EINVAL;
+                goto done;
+            }
+            break;
+        case BE_REQ_BY_CERT:
+            if (req_input->type == REQ_INP_CERT) {
+            ret = ber_printf(ber, "{ees}", INP_CERT, request_type,
+                                           req_input->inp.cert);
+            } else {
+                DEBUG(SSSDBG_OP_FAILURE, "Unexpected input type [%d].\n",
+                                          req_input->type);
                 ret = EINVAL;
                 goto done;
             }
@@ -1483,6 +1495,11 @@ static void ipa_s2n_get_user_done(struct tevent_req *subreq)
     talloc_zfree(subreq);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "s2n exop request failed.\n");
+        if (state->req_input->type == REQ_INP_CERT) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "Maybe the server does not support lookups by "
+                  "certificates.\n");
+        }
         goto done;
     }
 
diff --git a/src/providers/ipa/ipa_subdomains.h b/src/providers/ipa/ipa_subdomains.h
index 23c3b7e3c..9eb841b02 100644
--- a/src/providers/ipa/ipa_subdomains.h
+++ b/src/providers/ipa/ipa_subdomains.h
@@ -116,7 +116,8 @@ int ipa_ad_subdom_init(struct be_ctx *be_ctx,
 enum req_input_type {
     REQ_INP_NAME,
     REQ_INP_ID,
-    REQ_INP_SECID
+    REQ_INP_SECID,
+    REQ_INP_CERT
 };
 
 struct req_input {
@@ -125,6 +126,7 @@ struct req_input {
         const char *name;
         uint32_t id;
         const char *secid;
+        const char *cert;
     } inp;
 };
 
diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
index f7a01c28e..64383e186 100644
--- a/src/providers/ipa/ipa_subdomains_id.c
+++ b/src/providers/ipa/ipa_subdomains_id.c
@@ -492,10 +492,23 @@ static void ipa_get_subdom_acct_connected(struct tevent_req *subreq)
             }
             break;
         case BE_FILTER_CERT:
-            DEBUG(SSSDBG_OP_FAILURE, "Lookup by certificate not supported yet.\n");
-            state->dp_error = dp_error;
-            tevent_req_error(req, EINVAL);
-            return;
+            if (sdap_is_extension_supported(sdap_id_op_handle(state->op),
+                                            EXOP_SID2NAME_V1_OID)) {
+                req_input->type = REQ_INP_CERT;
+                req_input->inp.cert = talloc_strdup(req_input, state->filter);
+                if (req_input->inp.cert == NULL) {
+                    DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+                    tevent_req_error(req, ENOMEM);
+                    return;
+                }
+            } else {
+                DEBUG(SSSDBG_OP_FAILURE,
+                      "Lookup by certificate not supported by the server.\n");
+                state->dp_error = DP_ERR_OK;
+                tevent_req_error(req, EINVAL);
+                return;
+            }
+            break;
         default:
             DEBUG(SSSDBG_OP_FAILURE, "Invalid sub-domain filter type.\n");
             state->dp_error = dp_error;

From 7eba8f92b3211835b7e63ddbf6063cca8b9d3ac0 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Mon, 25 Apr 2016 16:09:48 +0200
Subject: [PATCH 11/13] NSS: add SSS_NSS_GETNAMEBYCERT request

Reviewed-by: Jakub Hrozek <jhro...@redhat.com>
(cherry picked from commit 1a45124f3f300f9afdcb08eab0938e5e7d0534d9 with
backport fixes)
---
 Makefile.am                     |   3 +
 src/responder/nss/nsssrv_cmd.c  | 117 ++++++++++++++++++++++++++++++++
 src/sss_client/sss_cli.h        |   5 ++
 src/tests/cmocka/test_nss_srv.c | 145 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 270 insertions(+)

diff --git a/Makefile.am b/Makefile.am
index a747d832e..4e48886e1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1120,6 +1120,7 @@ sssd_nss_LDADD = \
     $(TDB_LIBS) \
     $(SSSD_LIBS) \
     libsss_idmap.la \
+    libsss_cert.la \
     $(SSSD_INTERNAL_LTLIBS)
 
 sssd_pam_SOURCES = \
@@ -1912,6 +1913,7 @@ nss_srv_tests_LDFLAGS = \
     -Wl,-wrap,sss_ncache_check_user \
     -Wl,-wrap,sss_ncache_check_uid \
     -Wl,-wrap,sss_ncache_check_sid \
+    -Wl,-wrap,sss_ncache_check_cert \
     -Wl,-wrap,sss_packet_get_body \
     -Wl,-wrap,sss_packet_get_cmd \
     -Wl,-wrap,sss_cmd_send_empty \
@@ -1921,6 +1923,7 @@ nss_srv_tests_LDADD = \
     $(SSSD_LIBS) \
     $(SSSD_INTERNAL_LTLIBS) \
     libsss_test_common.la \
+    libsss_cert.la \
     libsss_idmap.la
 
 EXTRA_pam_srv_tests_DEPENDENCIES = \
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 0f4ce3632..5b438fb31 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -23,12 +23,14 @@
 #include "util/sss_nss.h"
 #include "util/sss_cli_cmd.h"
 #include "util/crypto/sss_crypto.h"
+#include "util/cert.h"
 #include "responder/nss/nsssrv.h"
 #include "responder/nss/nsssrv_private.h"
 #include "responder/nss/nsssrv_netgroup.h"
 #include "responder/nss/nsssrv_services.h"
 #include "responder/nss/nsssrv_mmap_cache.h"
 #include "responder/common/negcache.h"
+#include "responder/common/responder_cache_req.h"
 #include "providers/data_provider.h"
 #include "confdb/confdb.h"
 #include "db/sysdb.h"
@@ -5523,6 +5525,115 @@ static int nss_cmd_getbysid(enum sss_cli_command cmd, struct cli_ctx *cctx)
     return nss_cmd_done(cmdctx, ret);
 }
 
+static void users_find_by_cert_done(struct tevent_req *req);
+
+static int nss_cmd_getbycert(enum sss_cli_command cmd, struct cli_ctx *cctx)
+{
+
+    struct tevent_req *req;
+    uint8_t *body;
+    size_t blen;
+    int ret;
+    const char *derb64;
+    char *pem_cert = NULL;
+    size_t pem_size;
+    struct nss_ctx *nctx;
+
+    nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
+
+    if (cmd != SSS_NSS_GETNAMEBYCERT) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command type [%d][%s].\n",
+              cmd, sss_cmd2str(cmd));
+        return EINVAL;
+    }
+
+    /* get certificate to query */
+    sss_packet_get_body(cctx->creq->in, &body, &blen);
+
+    /* if not terminated fail */
+    if (body[blen - 1] != '\0') {
+        return EINVAL;
+    }
+
+    derb64 = (const char *)body;
+
+    /* check input */
+    ret = sss_cert_derb64_to_pem(cctx, derb64, &pem_cert, &pem_size);
+    talloc_free(pem_cert);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "sss_cert_pem_to_derb64 failed.\n");
+        return ret;
+    }
+
+    req = cache_req_user_by_cert_send(cctx, cctx->rctx->ev, cctx->rctx,
+                                      nctx->ncache, nctx->neg_timeout,
+                                      0, NULL, derb64);
+    if (req == NULL) {
+        return ENOMEM;
+    }
+
+    tevent_req_set_callback(req, users_find_by_cert_done, cctx);
+
+    return EOK;
+}
+
+static void users_find_by_cert_done(struct tevent_req *req)
+{
+    struct cli_ctx *cctx;
+    struct sss_domain_info *domain;
+    struct ldb_result *result;
+    errno_t ret;
+
+    cctx = tevent_req_callback_data(req, struct cli_ctx);
+
+    ret = cache_req_user_by_cert_recv(cctx, req, &result, &domain, NULL);
+    talloc_zfree(req);
+    if (ret == ENOENT || result->count == 0) {
+        ret = ENOENT;
+        goto done;
+    } else if (ret != EOK) {
+        goto done;
+    }
+
+    if (result->count > 1) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Found more than 1 result with certficate search.\n");
+
+        ret = EINVAL;
+        goto done;
+    }
+
+    ret = sss_packet_new(cctx->creq, 0,
+                         sss_packet_get_cmd(cctx->creq->in),
+                         &cctx->creq->out);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "sss_packet_new failed.\n");
+        ret = EFAULT;
+        goto done;
+    }
+
+    ret = fill_name(cctx->creq->out, domain, SSS_ID_TYPE_UID, true,
+                    result->msgs[0]);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "fill_name failed.\n");
+        goto done;
+    }
+
+    ret = EOK;
+
+done:
+    if (ret == EOK) {
+        sss_packet_set_error(cctx->creq->out, EOK);
+        sss_cmd_done(cctx, NULL);
+    } else if (ret == ENOENT) {
+        sss_cmd_send_empty(cctx, NULL);
+    } else {
+        sss_cmd_send_error(cctx, ret);
+    }
+
+    return;
+}
+
 static int nss_cmd_getsidbyname(struct cli_ctx *cctx)
 {
     return nss_cmd_getbynam(SSS_NSS_GETSIDBYNAME, cctx);
@@ -5548,6 +5659,11 @@ static int nss_cmd_getorigbyname(struct cli_ctx *cctx)
     return nss_cmd_getbynam(SSS_NSS_GETORIGBYNAME, cctx);
 }
 
+static int nss_cmd_getnamebycert(struct cli_ctx *cctx)
+{
+    return nss_cmd_getbycert(SSS_NSS_GETNAMEBYCERT, cctx);
+}
+
 struct cli_protocol_version *register_cli_protocol_version(void)
 {
     static struct cli_protocol_version nss_cli_protocol_version[] = {
@@ -5584,6 +5700,7 @@ static struct sss_cmd_table nss_cmds[] = {
     {SSS_NSS_GETNAMEBYSID, nss_cmd_getnamebysid},
     {SSS_NSS_GETIDBYSID, nss_cmd_getidbysid},
     {SSS_NSS_GETORIGBYNAME, nss_cmd_getorigbyname},
+    {SSS_NSS_GETNAMEBYCERT, nss_cmd_getnamebycert},
     {SSS_CLI_NULL, NULL}
 };
 
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index f39ceba5e..17d8e4503 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -255,6 +255,11 @@ SSS_NSS_GETORIGBYNAME = 0x0115, /**< Takes a zero terminated fully qualified
                                      second the value. Hence the list should
                                      have an even number of strings, if not
                                      the whole list is invalid. */
+SSS_NSS_GETNAMEBYCERT = 0x0116, /**< Takes the zero terminated string
+                                     of the base64 encoded DER representation
+                                     of a X509 certificate and returns the zero
+                                     terminated fully qualified name of the
+                                     related object. */
 };
 
 /**
diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
index dcd2d8ab6..06b3c54c0 100644
--- a/src/tests/cmocka/test_nss_srv.c
+++ b/src/tests/cmocka/test_nss_srv.c
@@ -32,6 +32,8 @@
 #include "responder/nss/nsssrv_private.h"
 #include "sss_client/idmap/sss_nss_idmap.h"
 #include "util/util_sss_idmap.h"
+#include "util/crypto/sss_crypto.h"
+#include "util/crypto/nss/nss_util.h"
 #include "db/sysdb_private.h"   /* new_subdomain() */
 
 #define TESTS_PATH "tp_" BASE_FILE_STEM
@@ -196,6 +198,21 @@ int __wrap_sss_ncache_check_sid(struct sss_nc_ctx *ctx,
     return ret;
 }
 
+int __real_sss_ncache_check_cert(struct sss_nc_ctx *ctx,
+                                 int ttl, const char *cert);
+
+int __wrap_sss_ncache_check_cert(struct sss_nc_ctx *ctx,
+                                 int ttl, const char *cert)
+{
+    int ret;
+
+    ret = __real_sss_ncache_check_cert(ctx, ttl, cert);
+    if (ret == EEXIST) {
+        nss_test_ctx->ncache_hits++;
+    }
+    return ret;
+}
+
 /* Mock input from the client library */
 static void mock_input_user_or_group(const char *username)
 {
@@ -2991,6 +3008,124 @@ void test_nss_getnamebysid_update(void **state)
     assert_string_equal(shell, "/bin/ksh");
 }
 
+#define TEST_TOKEN_CERT \
+"MIIECTCCAvGgAwIBAgIBCDANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \
+"REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNTA2MjMx" \
+"NjMyMDdaFw0xNzA2MjMxNjMyMDdaMDIxEjAQBgNVBAoMCUlQQS5ERVZFTDEcMBoG" \
+"A1UEAwwTaXBhLWRldmVsLmlwYS5kZXZlbDCCASIwDQYJKoZIhvcNAQEBBQADggEP" \
+"ADCCAQoCggEBALXUq56VlY+Z0aWLLpFAjFfbElPBXGQsbZb85J3cGyPjaMHC9wS+" \
+"wjB6Ve4HmQyPLx8hbINdDmbawMHYQvTScLYfsqLtj0Lqw20sUUmedk+Es5Oh9VHo" \
+"nd8MavYx25Du2u+T0iSgNIDikXguiwCmtAj8VC49ebbgITcjJGzMmiiuJkV3o93Y" \
+"vvYF0VjLGDQbQWOy7IxzYJeNVJnZWKo67CHdok6qOrm9rxQt81rzwV/mGLbCMUbr" \
+"+N4M8URtd7EmzaYZQmNm//s2owFrCYMxpLiURPj+URZVuB72504/Ix7X0HCbA/AV" \
+"26J27fPY5nc8DMwfhUDCbTqPH/JEjd3mvY8CAwEAAaOCASYwggEiMB8GA1UdIwQY" \
+"MBaAFJOq+KAQmPEnNp8Wok23eGTdE7aDMDsGCCsGAQUFBwEBBC8wLTArBggrBgEF" \
+"BQcwAYYfaHR0cDovL2lwYS1jYS5pcGEuZGV2ZWwvY2Evb2NzcDAOBgNVHQ8BAf8E" \
+"BAMCBPAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMHQGA1UdHwRtMGsw" \
+"aaAxoC+GLWh0dHA6Ly9pcGEtY2EuaXBhLmRldmVsL2lwYS9jcmwvTWFzdGVyQ1JM" \
+"LmJpbqI0pDIwMDEOMAwGA1UECgwFaXBhY2ExHjAcBgNVBAMMFUNlcnRpZmljYXRl" \
+"IEF1dGhvcml0eTAdBgNVHQ4EFgQUFaDNd5a53QGpaw5m63hnwXicMQ8wDQYJKoZI" \
+"hvcNAQELBQADggEBADH7Nj00qqGhGJeXJQAsepqSskz/wooqXh8vgVyb8SS4N0/c" \
+"0aQtVmY81xamlXE12ZFpwDX43d+EufBkwCUKFX/+8JFDd2doAyeJxv1xM22kKRpc" \
+"AqITPgMsa9ToGMWxjbVpc/X/5YfZixWPF0/eZUTotBj9oaR039UrhGfyN7OguF/G" \
+"rzmxtB5y4ZrMpcD/Oe90mkd9HY7sA/fB8OWOUgeRfQoh97HNS0UiDWsPtfxmjQG5" \
+"zotpoBIZmdH+ipYsu58HohHVlM9Wi5H4QmiiXl+Soldkq7eXYlafcmT7wv8+cKwz" \
+"Nz0Tm3+eYpFqRo3skr6QzXi525Jkg3r6r+kkhxU="
+
+static int test_nss_getnamebycert_check(uint32_t status, uint8_t *body, size_t blen)
+{
+    size_t rp = 2 * sizeof(uint32_t); /* num_results and reserved */
+    uint32_t id_type;
+    const char *name;
+
+    assert_int_equal(status, EOK);
+
+    SAFEALIGN_COPY_UINT32(&id_type, body + rp, &rp);
+    assert_int_equal(id_type, SSS_ID_TYPE_UID);
+
+    name = (const char *)body + rp;
+    assert_string_equal(name, "testcertuser");
+
+    return EOK;
+}
+
+static void test_nss_getnamebycert(void **state)
+{
+    errno_t ret;
+    struct sysdb_attrs *attrs;
+    unsigned char *der = NULL;
+    size_t der_size;
+
+    attrs = sysdb_new_attrs(nss_test_ctx);
+    assert_non_null(attrs);
+
+    der = sss_base64_decode(nss_test_ctx, TEST_TOKEN_CERT, &der_size);
+    assert_non_null(der);
+
+    ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_CERT, der, der_size);
+    talloc_free(der);
+    assert_int_equal(ret, EOK);
+
+    /* Prime the cache with a valid user */
+    ret = sysdb_add_user(nss_test_ctx->tctx->dom,
+                         "testcertuser", 23456, 6890, "test cert user",
+                         "/home/testcertuser", "/bin/sh", NULL,
+                         attrs, 300, 0);
+    assert_int_equal(ret, EOK);
+    talloc_free(attrs);
+
+    mock_input_user_or_group(TEST_TOKEN_CERT);
+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETNAMEBYCERT);
+    mock_fill_bysid();
+
+    /* Query for that user, call a callback when command finishes */
+    /* Should go straight to back end, without contacting DP */
+    set_cmd_cb(test_nss_getnamebycert_check);
+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETNAMEBYCERT,
+                          nss_test_ctx->nss_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with EOK */
+    ret = test_ev_loop(nss_test_ctx->tctx);
+    assert_int_equal(ret, EOK);
+}
+
+void test_nss_getnamebycert_neg(void **state)
+{
+    errno_t ret;
+
+    mock_input_user_or_group(TEST_TOKEN_CERT);
+    mock_account_recv_simple();
+
+    assert_int_equal(nss_test_ctx->ncache_hits, 0);
+
+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETNAMEBYCERT,
+                          nss_test_ctx->nss_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with ENOENT */
+    ret = test_ev_loop(nss_test_ctx->tctx);
+    assert_int_equal(ret, ENOENT);
+    assert_int_equal(nss_test_ctx->ncache_hits, 0);
+
+    /* Test that subsequent search for a nonexistent user yields
+     * ENOENT and Account callback is not called, on the other hand
+     * the ncache functions will be called
+     */
+    nss_test_ctx->tctx->done = false;
+
+    mock_input_user_or_group(TEST_TOKEN_CERT);
+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETNAMEBYCERT,
+                          nss_test_ctx->nss_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with ENOENT */
+    ret = test_ev_loop(nss_test_ctx->tctx);
+    assert_int_equal(ret, ENOENT);
+    /* Negative cache was hit this time */
+    assert_int_equal(nss_test_ctx->ncache_hits, 1);
+}
+
 int main(int argc, const char *argv[])
 {
     int rv;
@@ -3104,6 +3239,10 @@ int main(int argc, const char *argv[])
                                         nss_test_setup, nss_test_teardown),
         cmocka_unit_test_setup_teardown(test_nss_getnamebysid_update,
                                         nss_test_setup, nss_test_teardown),
+        cmocka_unit_test_setup_teardown(test_nss_getnamebycert_neg,
+                                        nss_test_setup, nss_test_teardown),
+        cmocka_unit_test_setup_teardown(test_nss_getnamebycert,
+                                        nss_test_setup, nss_test_teardown),
     };
 
     /* Set debug level to invalid value so we can deside if -d 0 was used. */
@@ -3133,5 +3272,11 @@ int main(int argc, const char *argv[])
     if (rv == 0 && !no_cleanup) {
         test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME);
     }
+
+#ifdef HAVE_NSS
+    /* Cleanup NSS and NSPR to make valgrind happy. */
+    nspr_nss_cleanup();
+#endif
+
     return rv;
 }

From 24e1d8eed09dac6586ce5f0d2ada5a31c4294e9c Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Tue, 26 Apr 2016 13:13:43 +0200
Subject: [PATCH 12/13] nss-idmap: add sss_nss_getnamebycert()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Reviewed-by: Jakub Hrozek <jhro...@redhat.com>
Reviewed-by: Lukáš Slebodník <lsleb...@redhat.com>
(cherry picked from commit 9c88f837ffacf6548c13825589b327de1a5525f3)
---
 Makefile.am                                |  2 +-
 contrib/sssd.spec.in                       |  8 ++---
 src/python/pysss_nss_idmap.c               | 47 ++++++++++++++++++++++++++++--
 src/responder/nss/nsssrv_cmd.c             |  1 +
 src/sss_client/idmap/sss_nss_idmap.c       | 26 ++++++++++++++++-
 src/sss_client/idmap/sss_nss_idmap.exports |  6 ++++
 src/sss_client/idmap/sss_nss_idmap.h       | 15 ++++++++++
 7 files changed, 97 insertions(+), 8 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 4e48886e1..9f5451796 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -979,7 +979,7 @@ libsss_nss_idmap_la_LIBADD = \
     $(CLIENT_LIBS)
 libsss_nss_idmap_la_LDFLAGS = \
     -Wl,--version-script,$(srcdir)/src/sss_client/idmap/sss_nss_idmap.exports \
-    -version-info 1:0:1
+    -version-info 2:0:2
 
 dist_noinst_DATA += src/sss_client/idmap/sss_nss_idmap.exports
 
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index 58431fbd7..a7399980e 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -454,23 +454,23 @@ used by Python applications.
 %endif
 
 %package -n libsss_nss_idmap
-Summary: Library for SID based lookups
+Summary: Library for SID and certificate based lookups
 Group: Development/Libraries
 License: LGPLv3+
 Requires(post): /sbin/ldconfig
 Requires(postun): /sbin/ldconfig
 
 %description -n libsss_nss_idmap
-Utility library for SID based lookups
+Utility library for SID and certificate based lookups
 
 %package -n libsss_nss_idmap-devel
-Summary: Library for SID based lookups
+Summary: Library for SID and certificate based lookups
 Group: Development/Libraries
 License: LGPLv3+
 Requires: libsss_nss_idmap = %{version}-%{release}
 
 %description -n libsss_nss_idmap-devel
-Utility library for SID based lookups
+Utility library for SID and certificate based lookups
 
 %package -n python-libsss_nss_idmap
 Summary: Python2 bindings for libsss_nss_idmap
diff --git a/src/python/pysss_nss_idmap.c b/src/python/pysss_nss_idmap.c
index a0561a05d..aa290f6cf 100644
--- a/src/python/pysss_nss_idmap.c
+++ b/src/python/pysss_nss_idmap.c
@@ -33,7 +33,8 @@ enum lookup_type {
     SIDBYNAME,
     SIDBYID,
     NAMEBYSID,
-    IDBYSID
+    IDBYSID,
+    NAMEBYCERT
 };
 
 static int add_dict(PyObject *py_result, PyObject *key, PyObject *res_type,
@@ -166,6 +167,28 @@ static int do_getsidbyid(PyObject *py_result, PyObject *py_id)
     return ret;
 }
 
+static int do_getnamebycert(PyObject *py_result, PyObject *py_cert)
+{
+    int ret;
+    const char *cert;
+    char *name = NULL;
+    enum sss_id_type id_type;
+
+    cert = py_string_or_unicode_as_string(py_cert);
+    if (cert == NULL) {
+        return EINVAL;
+    }
+
+    ret = sss_nss_getnamebycert(cert, &name, &id_type);
+    if (ret == 0) {
+        ret = add_dict(py_result, py_cert, PyBytes_FromString(SSS_NAME_KEY),
+                       PyUnicode_FromString(name), PYNUMBER_FROMLONG(id_type));
+    }
+    free(name);
+
+    return ret;
+}
+
 static int do_getidbysid(PyObject *py_result, PyObject *py_sid)
 {
     const char *sid;
@@ -203,6 +226,9 @@ static int do_lookup(enum lookup_type type, PyObject *py_result,
     case IDBYSID:
         return do_getidbysid(py_result, py_inp);
         break;
+    case NAMEBYCERT:
+        return do_getnamebycert(py_result, py_inp);
+        break;
     default:
         return ENOSYS;
     }
@@ -260,7 +286,7 @@ static PyObject *check_args(enum lookup_type type, PyObject *args)
         case ENOENT: /* nothing found, return empty dict */
             break;
         case EINVAL:
-            PyErr_Format(PyExc_ValueError, "Unable to retrieve argument\n");
+            PyErr_Format(PyExc_ValueError, "Unable to retrieve result\n");
             Py_XDECREF(py_result);
             return NULL;
             break;
@@ -339,6 +365,21 @@ static PyObject * py_getidbysid(PyObject *module, PyObject *args)
     return check_args(IDBYSID, args);
 }
 
+PyDoc_STRVAR(getnamebycert_doc,
+"getnamebycert(sid or list/tuple of certificates) -> dict(sid => dict(results))\n\
+\n\
+Returns a dictionary with a dictonary of results for each given certificates.\n\
+The result dictonary contain the name and the type of the object which can be\n\
+accessed with the key constants NAME_KEY and TYPE_KEY, respectively.\n\
+\n\
+NOTE: getnamebycert currently works only with id_provider set as \"ad\" or \"ipa\""
+);
+
+static PyObject * py_getnamebycert(PyObject *module, PyObject *args)
+{
+    return check_args(NAMEBYCERT, args);
+}
+
 static PyMethodDef methods[] = {
     { sss_py_const_p(char, "getsidbyname"), (PyCFunction) py_getsidbyname,
       METH_VARARGS, getsidbyname_doc },
@@ -348,6 +389,8 @@ static PyMethodDef methods[] = {
       METH_VARARGS, getnamebysid_doc },
     { sss_py_const_p(char, "getidbysid"), (PyCFunction) py_getidbysid,
       METH_VARARGS, getidbysid_doc },
+    { sss_py_const_p(char, "getnamebycert"), (PyCFunction) py_getnamebycert,
+      METH_VARARGS, getnamebycert_doc },
     { NULL,NULL, 0, NULL }
 };
 
diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 5b438fb31..ef6f06145 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -5556,6 +5556,7 @@ static int nss_cmd_getbycert(enum sss_cli_command cmd, struct cli_ctx *cctx)
     }
 
     derb64 = (const char *)body;
+    DEBUG(SSSDBG_TRACE_ALL, "cert [%s]\n", derb64);
 
     /* check input */
     ret = sss_cert_derb64_to_pem(cctx, derb64, &pem_cert, &pem_size);
diff --git a/src/sss_client/idmap/sss_nss_idmap.c b/src/sss_client/idmap/sss_nss_idmap.c
index 55d8043bd..fa5a499e3 100644
--- a/src/sss_client/idmap/sss_nss_idmap.c
+++ b/src/sss_client/idmap/sss_nss_idmap.c
@@ -159,7 +159,8 @@ static int sss_nss_getyyybyxxx(union input inp, enum sss_cli_command cmd ,
     case SSS_NSS_GETNAMEBYSID:
     case SSS_NSS_GETIDBYSID:
     case SSS_NSS_GETORIGBYNAME:
-        ret = sss_strnlen(inp.str, SSS_NAME_MAX, &inp_len);
+    case SSS_NSS_GETNAMEBYCERT:
+        ret = sss_strnlen(inp.str, 2048, &inp_len);
         if (ret != EOK) {
             return EINVAL;
         }
@@ -209,6 +210,7 @@ static int sss_nss_getyyybyxxx(union input inp, enum sss_cli_command cmd ,
     case SSS_NSS_GETSIDBYID:
     case SSS_NSS_GETSIDBYNAME:
     case SSS_NSS_GETNAMEBYSID:
+    case SSS_NSS_GETNAMEBYCERT:
         if (data_len <= 1 || repbuf[replen - 1] != '\0') {
             ret = EBADMSG;
             goto done;
@@ -368,3 +370,25 @@ int sss_nss_getorigbyname(const char *fq_name, struct sss_nss_kv **kv_list,
 
     return ret;
 }
+
+int sss_nss_getnamebycert(const char *cert, char **fq_name,
+                          enum sss_id_type *type)
+{
+    int ret;
+    union input inp;
+    struct output out;
+
+    if (fq_name == NULL || cert == NULL || *cert == '\0') {
+        return EINVAL;
+    }
+
+    inp.str = cert;
+
+    ret = sss_nss_getyyybyxxx(inp, SSS_NSS_GETNAMEBYCERT, &out);
+    if (ret == EOK) {
+        *fq_name = out.d.str;
+        *type = out.type;
+    }
+
+    return ret;
+}
diff --git a/src/sss_client/idmap/sss_nss_idmap.exports b/src/sss_client/idmap/sss_nss_idmap.exports
index 8aa470241..bd5d80212 100644
--- a/src/sss_client/idmap/sss_nss_idmap.exports
+++ b/src/sss_client/idmap/sss_nss_idmap.exports
@@ -19,3 +19,9 @@ SSS_NSS_IDMAP_0.1.0 {
         sss_nss_getorigbyname;
         sss_nss_free_kv;
 } SSS_NSS_IDMAP_0.0.1;
+
+SSS_NSS_IDMAP_0.2.0 {
+    # public functions
+    global:
+        sss_nss_getnamebycert;
+} SSS_NSS_IDMAP_0.1.0;
diff --git a/src/sss_client/idmap/sss_nss_idmap.h b/src/sss_client/idmap/sss_nss_idmap.h
index 78a8a11c1..8a6299194 100644
--- a/src/sss_client/idmap/sss_nss_idmap.h
+++ b/src/sss_client/idmap/sss_nss_idmap.h
@@ -123,6 +123,21 @@ int sss_nss_getidbysid(const char *sid, uint32_t *id,
 int sss_nss_getorigbyname(const char *fq_name, struct sss_nss_kv **kv_list,
                           enum sss_id_type *type);
 
+/**
+ * @brief Return the fully qualified name for the given base64 encoded
+ * X.509 certificate in DER format
+ *
+ * @param[in] cert     base64 encoded certificate
+ * @param[out] fq_name Fully qualified name of a user or a group,
+ *                     must be freed by the caller
+ * @param[out] type    Type of the object related to the SID
+ *
+ * @return
+ *  - see #sss_nss_getsidbyname
+ */
+int sss_nss_getnamebycert(const char *cert, char **fq_name,
+                          enum sss_id_type *type);
+
 /**
  * @brief Free key-value list returned by sss_nss_getorigbyname()
  *

From a2f50fc5083d77f2f0f6d1f76ea26f9eb7d19232 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Mon, 8 Jan 2018 18:23:50 +0100
Subject: [PATCH 13/13] ipa: remove SYSDB_USER_CERT from sub-domain users

If there are no certificates returned for a sub-domain user from the IPA
server to the client we should make sure they are not present in the
client's cache anymore and remove the whole attribute from the cached
user entry.

Related to https://pagure.io/SSSD/sssd/issue/3603
---
 src/providers/ipa/ipa_s2n_exop.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/providers/ipa/ipa_s2n_exop.c b/src/providers/ipa/ipa_s2n_exop.c
index 22e99cbef..5b5b9eed2 100644
--- a/src/providers/ipa/ipa_s2n_exop.c
+++ b/src/providers/ipa/ipa_s2n_exop.c
@@ -1798,7 +1798,11 @@ static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom,
     char ** exop_grouplist;
     struct ldb_message *msg;
     struct ldb_message_element *el = NULL;
-    const char *missing[] = {NULL, NULL};
+
+    /* SYSDB_ORIG_MEMBEROF and/or SYSDB_USER_CERT might be missing, the
+     * missing array will 3 entries including the trailing NULL at the end. */
+    size_t missing_count = 0;
+    const char *missing[] = {NULL, NULL, NULL};
 
     tmp_ctx = talloc_new(NULL);
     if (tmp_ctx == NULL) {
@@ -2031,7 +2035,13 @@ static errno_t ipa_s2n_save_objects(struct sss_domain_info *dom,
             ret = sysdb_attrs_get_el_ext(attrs->sysdb_attrs,
                                          SYSDB_ORIG_MEMBEROF, false, &el);
             if (ret == ENOENT) {
-                missing[0] = SYSDB_ORIG_MEMBEROF;
+                missing[missing_count++] = SYSDB_ORIG_MEMBEROF;
+            }
+
+            ret = sysdb_attrs_get_el_ext(attrs->sysdb_attrs,
+                                         SYSDB_USER_CERT, false, &el);
+            if (ret == ENOENT) {
+                missing[missing_count++] = SYSDB_USER_CERT;
             }
 
             ret = sysdb_transaction_start(dom->sysdb);
_______________________________________________
sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org
To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org

Reply via email to