>From 67720edbd993ffa3ffc1d16afeb234cf7b9f87ce Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Tue, 26 May 2015 14:29:17 +0200
Subject: [PATCH 7/7] IFP: add FindByCertificate method for User objects

Related to https://fedorahosted.org/sssd/ticket/2596
---
 Makefile.am                                |  4 +-
 src/responder/common/responder.h           |  3 +-
 src/responder/common/responder_cache_req.c | 64 +++++++++++++++++++++-
 src/responder/common/responder_cache_req.h | 16 +++++-
 src/responder/common/responder_dp.c        | 11 ++++
 src/responder/ifp/ifp_iface.c              |  1 +
 src/responder/ifp/ifp_iface.xml            |  4 ++
 src/responder/ifp/ifp_iface_generated.c    | 26 +++++++++
 src/responder/ifp/ifp_iface_generated.h    |  5 ++
 src/responder/ifp/ifp_users.c              | 87 ++++++++++++++++++++++++++++++
 src/responder/ifp/ifp_users.h              |  4 ++
 src/sbus/sssd_dbus_errors.h                |  1 +
 12 files changed, 221 insertions(+), 5 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 72745944b3543aba375be1a4629bb09ffc362857..4ae8016d29c94d879cf79eff6d66021fb086f866 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1129,7 +1129,9 @@ sssd_ifp_CFLAGS = \
     $(AM_CFLAGS)
 sssd_ifp_LDADD = \
     $(SSSD_LIBS) \
-    $(SSSD_INTERNAL_LTLIBS)
+    $(SSSD_INTERNAL_LTLIBS) \
+    libsss_cert.la \
+    $(NULL)
 dist_dbuspolicy_DATA = \
     src/responder/ifp/org.freedesktop.sssd.infopipe.conf
 dist_dbusservice_DATA = \
diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
index 9c7a73809a3de8859aa546c2a7d31620fea39ac9..bd0250d52aae1efdac3bc0847f692542c6cfb6d2 100644
--- a/src/responder/common/responder.h
+++ b/src/responder/common/responder.h
@@ -281,7 +281,8 @@ enum sss_dp_acct_type {
     SSS_DP_NETGR,
     SSS_DP_SERVICES,
     SSS_DP_SECID,
-    SSS_DP_USER_AND_GROUP
+    SSS_DP_USER_AND_GROUP,
+    SSS_DP_CERT
 };
 
 struct tevent_req *
diff --git a/src/responder/common/responder_cache_req.c b/src/responder/common/responder_cache_req.c
index 7ba257276b064d923b6144f45ade59cac48849d5..eae6f7823dfa96c4b0fa4b8da034913629ba91d9 100644
--- a/src/responder/common/responder_cache_req.c
+++ b/src/responder/common/responder_cache_req.c
@@ -34,6 +34,7 @@ struct cache_req_input {
     /* Provided input. */
     const char *orig_name;
     uint32_t id;
+    const char *cert;
 
     /* Data Provider request type resolved from @type.
      * FIXME: This is currently needed for data provider calls. We should
@@ -82,6 +83,17 @@ cache_req_input_create(TALLOC_CTX *mem_ctx,
             goto fail;
         }
         break;
+    case CACHE_REQ_USER_BY_CERT:
+        if (name == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Bug: certificate cannot be NULL!\n");
+            goto fail;
+        }
+
+        input->cert = talloc_strdup(input, name);
+        if (input->cert == NULL) {
+            goto fail;
+        }
+        break;
     case CACHE_REQ_USER_BY_ID:
     case CACHE_REQ_GROUP_BY_ID:
         if (id == 0) {
@@ -108,6 +120,9 @@ cache_req_input_create(TALLOC_CTX *mem_ctx,
     case CACHE_REQ_INITGROUPS:
         input->dp_type = SSS_DP_INITGROUPS;
         break;
+    case CACHE_REQ_USER_BY_CERT:
+        input->dp_type = SSS_DP_CERT;
+        break;
     }
 
     return input;
@@ -192,6 +207,13 @@ cache_req_input_set_domain(struct cache_req_input *input,
             goto done;
         }
         break;
+    case CACHE_REQ_USER_BY_CERT:
+        fqn = talloc_asprintf(tmp_ctx, "CERT:%s@%s", input->cert, domain->name);
+        if (fqn == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+        break;
     }
 
     input->domain = domain;
@@ -227,6 +249,9 @@ static errno_t cache_req_check_ncache(struct cache_req_input *input,
     case CACHE_REQ_GROUP_BY_ID:
         ret = sss_ncache_check_gid(ncache, neg_timeout, input->id);
         break;
+    case CACHE_REQ_USER_BY_CERT:
+        ret = sss_ncache_check_cert(ncache, neg_timeout, input->cert);
+        break;
     }
 
     if (ret == EEXIST) {
@@ -254,6 +279,7 @@ static void cache_req_add_to_ncache(struct cache_req_input *input,
         break;
     case CACHE_REQ_USER_BY_ID:
     case CACHE_REQ_GROUP_BY_ID:
+    case CACHE_REQ_USER_BY_CERT:
         /* Nothing to do. Those types must be unique among all domains so
          * the don't contain domain part. Therefore they must be set only
          * if all domains are search and the entry is not found. */
@@ -290,6 +316,9 @@ static void cache_req_add_to_ncache_global(struct cache_req_input *input,
     case CACHE_REQ_GROUP_BY_ID:
         ret = sss_ncache_set_gid(ncache, false, input->id);
         break;
+    case CACHE_REQ_USER_BY_CERT:
+        ret = sss_ncache_set_cert(ncache, false, input->cert);
+        break;
     }
 
     if (ret != EOK) {
@@ -338,6 +367,11 @@ static errno_t cache_req_get_object(TALLOC_CTX *mem_ctx,
         ret = sysdb_initgroups_with_views(mem_ctx, input->domain,
                                           input->dom_objname, &result);
         break;
+    case CACHE_REQ_USER_BY_CERT:
+        one_item_only = true;
+        ret = sysdb_search_user_by_cert(mem_ctx, input->domain,
+                                        input->cert, &result);
+        break;
     }
 
     if (ret != EOK) {
@@ -461,6 +495,7 @@ static errno_t cache_req_cache_check(struct tevent_req *req)
     const char *extra_flag = NULL;
     uint64_t cache_expire = 0;
     errno_t ret;
+    const char *search_str;
 
     state = tevent_req_data(req, struct cache_req_cache_state);
 
@@ -479,6 +514,8 @@ static errno_t cache_req_cache_check(struct tevent_req *req)
                                   state->cache_refresh_percent, cache_expire);
     }
 
+    search_str = (state->input->type == CACHE_REQ_USER_BY_CERT) ?
+                                 state->input->cert : state->input->dom_objname;
     switch (ret) {
     case EOK:
         DEBUG(SSSDBG_TRACE_FUNC, "Cached entry is valid, returning...\n");
@@ -492,7 +529,7 @@ static errno_t cache_req_cache_check(struct tevent_req *req)
         subreq = sss_dp_get_account_send(state, state->rctx,
                                          state->input->domain, true,
                                          state->input->dp_type,
-                                         state->input->dom_objname,
+                                         search_str,
                                          state->input->id, NULL);
         if (subreq == NULL) {
             DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory sending out-of-band "
@@ -514,7 +551,7 @@ static errno_t cache_req_cache_check(struct tevent_req *req)
         subreq = sss_dp_get_account_send(state, state->rctx,
                                          state->input->domain, true,
                                          state->input->dp_type,
-                                         state->input->dom_objname,
+                                         search_str,
                                          state->input->id, extra_flag);
         if (subreq == NULL) {
             DEBUG(SSSDBG_CRIT_FAILURE,
@@ -923,6 +960,29 @@ cache_req_user_by_id_send(TALLOC_CTX *mem_ctx,
 }
 
 struct tevent_req *
+cache_req_user_by_cert_send(TALLOC_CTX *mem_ctx,
+                            struct tevent_context *ev,
+                            struct resp_ctx *rctx,
+                            struct sss_nc_ctx *ncache,
+                            int neg_timeout,
+                            int cache_refresh_percent,
+                            const char *domain,
+                            const char *pem_cert)
+{
+    struct cache_req_input *input;
+
+    input = cache_req_input_create(mem_ctx, CACHE_REQ_USER_BY_CERT,
+                                   pem_cert, 0);
+    if (input == NULL) {
+        return NULL;
+    }
+
+    return cache_req_steal_input_and_send(mem_ctx, ev, rctx, ncache,
+                                          neg_timeout, cache_refresh_percent,
+                                          domain, input);
+}
+
+struct tevent_req *
 cache_req_group_by_name_send(TALLOC_CTX *mem_ctx,
                              struct tevent_context *ev,
                              struct resp_ctx *rctx,
diff --git a/src/responder/common/responder_cache_req.h b/src/responder/common/responder_cache_req.h
index 088e8efe0ceefe49510bc5eab37e7825b667e5f3..9b862a8b7751bfd938205184b4d6452a75f2684e 100644
--- a/src/responder/common/responder_cache_req.h
+++ b/src/responder/common/responder_cache_req.h
@@ -32,7 +32,8 @@ enum cache_req_type {
     CACHE_REQ_USER_BY_ID,
     CACHE_REQ_GROUP_BY_NAME,
     CACHE_REQ_GROUP_BY_ID,
-    CACHE_REQ_INITGROUPS
+    CACHE_REQ_INITGROUPS,
+    CACHE_REQ_USER_BY_CERT
 };
 
 struct cache_req_input;
@@ -90,6 +91,19 @@ cache_req_user_by_id_send(TALLOC_CTX *mem_ctx,
     cache_req_recv(mem_ctx, req, _result, _domain, NULL)
 
 struct tevent_req *
+cache_req_user_by_cert_send(TALLOC_CTX *mem_ctx,
+                          struct tevent_context *ev,
+                          struct resp_ctx *rctx,
+                          struct sss_nc_ctx *ncache,
+                          int neg_timeout,
+                          int cache_refresh_percent,
+                          const char *domain,
+                          const char *pem_cert);
+
+#define cache_req_user_by_cert_recv(mem_ctx, req, _result, _domain, _name) \
+    cache_req_recv(mem_ctx, req, _result, _domain, _name)
+
+struct tevent_req *
 cache_req_group_by_name_send(TALLOC_CTX *mem_ctx,
                              struct tevent_context *ev,
                              struct resp_ctx *rctx,
diff --git a/src/responder/common/responder_dp.c b/src/responder/common/responder_dp.c
index 853b3eae3877a8b6b2ffb310a0fb9ccf017498ae..f752c94c37fd0efa0d072ef8c567bd875a08e6f7 100644
--- a/src/responder/common/responder_dp.c
+++ b/src/responder/common/responder_dp.c
@@ -548,6 +548,9 @@ sss_dp_get_account_msg(void *pvt)
         case SSS_DP_USER_AND_GROUP:
             be_type = BE_REQ_USER_AND_GROUP;
             break;
+        case SSS_DP_CERT:
+            be_type = BE_REQ_BY_CERT;
+            break;
     }
 
     if (info->fast_reply) {
@@ -563,6 +566,14 @@ sss_dp_get_account_msg(void *pvt)
                 filter = talloc_asprintf(info, "%s=%s", DP_SEC_ID,
                                                info->opt_name);
             }
+        } else if (info->type == SSS_DP_CERT) {
+            if (info->extra) {
+                filter = talloc_asprintf(info, "%s=%s:%s", DP_CERT,
+                                               info->opt_name, info->extra);
+            } else {
+                filter = talloc_asprintf(info, "%s=%s", DP_CERT,
+                                               info->opt_name);
+            }
         } else {
             if (info->extra) {
                 filter = talloc_asprintf(info, "name=%s:%s",
diff --git a/src/responder/ifp/ifp_iface.c b/src/responder/ifp/ifp_iface.c
index ce987b6075ee886e7608455600e0a788c30c9741..a9f7d998c609891992ac7397a0cf6cf680054a57 100644
--- a/src/responder/ifp/ifp_iface.c
+++ b/src/responder/ifp/ifp_iface.c
@@ -82,6 +82,7 @@ struct iface_ifp_users iface_ifp_users = {
     { &iface_ifp_users_meta, 0 },
     .FindByName = ifp_users_find_by_name,
     .FindByID = ifp_users_find_by_id,
+    .FindByCertificate = ifp_users_find_by_cert,
     .ListByName = ifp_users_list_by_name,
     .ListByDomainAndName = ifp_users_list_by_domain_and_name
 };
diff --git a/src/responder/ifp/ifp_iface.xml b/src/responder/ifp/ifp_iface.xml
index fbd3d3f5ac812a21c8768e28188217edbc83300b..da193c5cd3b82e724bc9c6cf5bae8d3cc3e7b98c 100644
--- a/src/responder/ifp/ifp_iface.xml
+++ b/src/responder/ifp/ifp_iface.xml
@@ -152,6 +152,10 @@
             <arg name="id" type="u" direction="in" />
             <arg name="result" type="o" direction="out" />
         </method>
+        <method name="FindByCertificate">
+            <arg name="pem_cert" type="s" direction="in" />
+            <arg name="result" type="o" direction="out" />
+        </method>
         <method name="ListByName">
             <arg name="name_filter" type="s" direction="in" />
             <arg name="limit" type="u" direction="in" />
diff --git a/src/responder/ifp/ifp_iface_generated.c b/src/responder/ifp/ifp_iface_generated.c
index ff00be8eaaedc28e37d39cdc85262c8041dafa0b..aced9e42b58f3420efb2f74d9d03d3db12c213e3 100644
--- a/src/responder/ifp/ifp_iface_generated.c
+++ b/src/responder/ifp/ifp_iface_generated.c
@@ -569,6 +569,25 @@ int iface_ifp_users_FindByID_finish(struct sbus_request *req, const char *arg_re
                                          DBUS_TYPE_INVALID);
 }
 
+/* arguments for org.freedesktop.sssd.infopipe.Users.FindByCertificate */
+const struct sbus_arg_meta iface_ifp_users_FindByCertificate__in[] = {
+    { "pem_cert", "s" },
+    { NULL, }
+};
+
+/* arguments for org.freedesktop.sssd.infopipe.Users.FindByCertificate */
+const struct sbus_arg_meta iface_ifp_users_FindByCertificate__out[] = {
+    { "result", "o" },
+    { NULL, }
+};
+
+int iface_ifp_users_FindByCertificate_finish(struct sbus_request *req, const char *arg_result)
+{
+   return sbus_request_return_and_finish(req,
+                                         DBUS_TYPE_OBJECT_PATH, &arg_result,
+                                         DBUS_TYPE_INVALID);
+}
+
 /* arguments for org.freedesktop.sssd.infopipe.Users.ListByName */
 const struct sbus_arg_meta iface_ifp_users_ListByName__in[] = {
     { "name_filter", "s" },
@@ -627,6 +646,13 @@ const struct sbus_method_meta iface_ifp_users__methods[] = {
         invoke_u_method,
     },
     {
+        "FindByCertificate", /* name */
+        iface_ifp_users_FindByCertificate__in,
+        iface_ifp_users_FindByCertificate__out,
+        offsetof(struct iface_ifp_users, FindByCertificate),
+        invoke_s_method,
+    },
+    {
         "ListByName", /* name */
         iface_ifp_users_ListByName__in,
         iface_ifp_users_ListByName__out,
diff --git a/src/responder/ifp/ifp_iface_generated.h b/src/responder/ifp/ifp_iface_generated.h
index 1bd2a448f3ee9af73091bd991a27f6b60f07a579..d8a4ac0f2126b3be27dd7902dfd02239f87867a4 100644
--- a/src/responder/ifp/ifp_iface_generated.h
+++ b/src/responder/ifp/ifp_iface_generated.h
@@ -58,6 +58,7 @@
 #define IFACE_IFP_USERS "org.freedesktop.sssd.infopipe.Users"
 #define IFACE_IFP_USERS_FINDBYNAME "FindByName"
 #define IFACE_IFP_USERS_FINDBYID "FindByID"
+#define IFACE_IFP_USERS_FINDBYCERTIFICATE "FindByCertificate"
 #define IFACE_IFP_USERS_LISTBYNAME "ListByName"
 #define IFACE_IFP_USERS_LISTBYDOMAINANDNAME "ListByDomainAndName"
 
@@ -209,6 +210,9 @@ int iface_ifp_users_FindByName_finish(struct sbus_request *req, const char *arg_
 /* finish function for FindByID */
 int iface_ifp_users_FindByID_finish(struct sbus_request *req, const char *arg_result);
 
+/* finish function for FindByCertificate */
+int iface_ifp_users_FindByCertificate_finish(struct sbus_request *req, const char *arg_result);
+
 /* finish function for ListByName */
 int iface_ifp_users_ListByName_finish(struct sbus_request *req, const char *arg_result[], int len_result);
 
@@ -237,6 +241,7 @@ struct iface_ifp_groups {
     struct sbus_vtable vtable; /* derive from sbus_vtable */
     int (*FindByName)(struct sbus_request *req, void *data, const char *arg_name);
     int (*FindByID)(struct sbus_request *req, void *data, uint32_t arg_id);
+    int (*FindByCertificate)(struct sbus_request *req, void *data, const char *arg_pem_cert);
     int (*ListByName)(struct sbus_request *req, void *data, const char *arg_name_filter, uint32_t arg_limit);
     int (*ListByDomainAndName)(struct sbus_request *req, void *data, const char *arg_domain_name, const char *arg_name_filter, uint32_t arg_limit);
 };
diff --git a/src/responder/ifp/ifp_users.c b/src/responder/ifp/ifp_users.c
index 26c1aa696e5fbc3856ff45b0eb2c88d6bfbd2340..9ecf7264fbc4cd973c179fae5c355a4f510a8373 100644
--- a/src/responder/ifp/ifp_users.c
+++ b/src/responder/ifp/ifp_users.c
@@ -25,6 +25,7 @@
 #include "db/sysdb.h"
 #include "util/util.h"
 #include "util/strtonum.h"
+#include "util/cert.h"
 #include "sbus/sssd_dbus_errors.h"
 #include "responder/common/responder.h"
 #include "responder/common/responder_cache_req.h"
@@ -221,6 +222,92 @@ done:
     return;
 }
 
+static void ifp_users_find_by_cert_done(struct tevent_req *req);
+
+int ifp_users_find_by_cert(struct sbus_request *sbus_req, void *data,
+                           const char *pem_cert)
+{
+    struct ifp_ctx *ctx;
+    struct tevent_req *req;
+    int ret;
+    char *derb64;
+    DBusError *error;
+
+    ctx = talloc_get_type(data, struct ifp_ctx);
+    if (ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Invalid pointer!\n");
+        return ERR_INTERNAL;
+    }
+
+    ret = sss_cert_pem_to_derb64(sbus_req, pem_cert, &derb64);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "sss_cert_pem_to_derb64 failed.\n");
+
+        if (ret == ENOMEM) {
+            return ret;
+        }
+
+        error = sbus_error_new(sbus_req, SBUS_ERROR_EINVAL,
+                               "Invalid certificate format");
+        sbus_request_fail_and_finish(sbus_req, error);
+        /* the connection is already terminated with an error message, hence
+         * we have to return EOK to not terminate the connection twice. */
+        return EOK;
+    }
+
+    req = cache_req_user_by_cert_send(sbus_req, ctx->rctx->ev, ctx->rctx,
+                                      ctx->ncache, ctx->neg_timeout, 0,
+                                      NULL, derb64);
+    if (req == NULL) {
+        return ENOMEM;
+    }
+
+    tevent_req_set_callback(req, ifp_users_find_by_cert_done, sbus_req);
+
+    return EOK;
+}
+
+static void ifp_users_find_by_cert_done(struct tevent_req *req)
+{
+    DBusError *error;
+    struct sbus_request *sbus_req;
+    struct sss_domain_info *domain;
+    struct ldb_result *result;
+    char *object_path;
+    errno_t ret;
+
+    sbus_req = tevent_req_callback_data(req, struct sbus_request);
+
+    ret = cache_req_user_by_cert_recv(sbus_req, req, &result, &domain, NULL);
+    talloc_zfree(req);
+    if (ret == ENOENT) {
+        error = sbus_error_new(sbus_req, SBUS_ERROR_NOT_FOUND,
+                               "User not found");
+        goto done;
+    } else if (ret != EOK) {
+        error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED, "Failed to fetch "
+                               "user [%d]: %s\n", ret, sss_strerror(ret));
+        goto done;
+    }
+
+    object_path = ifp_users_build_path_from_msg(sbus_req, domain,
+                                                result->msgs[0]);
+    if (object_path == NULL) {
+        error = sbus_error_new(sbus_req, SBUS_ERROR_INTERNAL,
+                               "Failed to compose object path");
+        goto done;
+    }
+
+done:
+    if (ret != EOK) {
+        sbus_request_fail_and_finish(sbus_req, error);
+        return;
+    }
+
+    iface_ifp_users_FindByCertificate_finish(sbus_req, object_path);
+    return;
+}
+
 int ifp_users_list_by_name(struct sbus_request *sbus_req,
                            void *data,
                            const char *filter,
diff --git a/src/responder/ifp/ifp_users.h b/src/responder/ifp/ifp_users.h
index 7027ef14e51e3ca2bd92368fdfc10315edbf2fa8..5e1baf95c81b4073b8f7f97865627e963fee20d8 100644
--- a/src/responder/ifp/ifp_users.h
+++ b/src/responder/ifp/ifp_users.h
@@ -43,6 +43,10 @@ int ifp_users_find_by_id(struct sbus_request *sbus_req,
                          void *data,
                          uint32_t id);
 
+int ifp_users_find_by_cert(struct sbus_request *sbus_req,
+                           void *data,
+                           const char *pem_cert);
+
 int ifp_users_list_by_name(struct sbus_request *sbus_req,
                            void *data,
                            const char *filter,
diff --git a/src/sbus/sssd_dbus_errors.h b/src/sbus/sssd_dbus_errors.h
index b2b2b233be4d2839d59cb32389505de2b393ba57..7f1656a274f3d2fd52ccd4cb1575ace78bc6d0d1 100644
--- a/src/sbus/sssd_dbus_errors.h
+++ b/src/sbus/sssd_dbus_errors.h
@@ -25,5 +25,6 @@
 
 #define SBUS_ERROR_INTERNAL "org.freedesktop.sssd.Error.Internal"
 #define SBUS_ERROR_NOT_FOUND "org.freedesktop.sssd.Error.NotFound"
+#define SBUS_ERROR_EINVAL "org.freedesktop.sssd.Error.InvalidArgument"
 
 #endif /* SSSD_DBUS_ERRORS_H_ */
-- 
1.9.3

