From c614e398a8f450f0b38baa390ae92064fd889b6a Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Sat, 21 Jan 2012 12:37:36 -0500
Subject: [PATCH 10/10] PROXY: add support for service lookups
 (non-enumeration)

---
 Makefile.am                          |    1 +
 src/providers/proxy/proxy.h          |   21 +++
 src/providers/proxy/proxy_id.c       |   24 +++
 src/providers/proxy/proxy_init.c     |   22 +++
 src/providers/proxy/proxy_services.c |  264 ++++++++++++++++++++++++++++++++++
 5 files changed, 332 insertions(+), 0 deletions(-)
 create mode 100644 src/providers/proxy/proxy_services.c

diff --git a/Makefile.am b/Makefile.am
index 2e47e96fbd06c454fa06c122f9714475808e3018..a8ae01d7c9c599765b99efba75e7465830b240c4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -949,6 +949,7 @@ libsss_proxy_la_SOURCES = \
     src/providers/proxy/proxy_init.c \
     src/providers/proxy/proxy_id.c \
     src/providers/proxy/proxy_netgroup.c \
+    src/providers/proxy/proxy_services.c \
     src/providers/proxy/proxy_auth.c
 libsss_proxy_la_CFLAGS = \
     $(AM_CFLAGS)
diff --git a/src/providers/proxy/proxy.h b/src/providers/proxy/proxy.h
index 41981fd85f44fa4cdfa40cc2833b972d82cdb5ab..15fdea42341eb1cafdf001e55df47429801a35ba 100644
--- a/src/providers/proxy/proxy.h
+++ b/src/providers/proxy/proxy.h
@@ -70,6 +70,17 @@ struct proxy_nss_ops {
     enum nss_status (*getnetgrent_r)(struct __netgrent *result, char *buffer,
                                      size_t buflen, int *errnop);
     enum nss_status (*endnetgrent)(struct __netgrent *result);
+
+    /* Services */
+    enum nss_status (*getservbyname_r)(const char *name,
+                                        const char *protocol,
+                                        struct servent *result,
+                                        char *buffer, size_t buflen,
+                                        int *errnop);
+    enum nss_status (*getservbyport_r)(int port, const char *protocol,
+                                        struct servent *result,
+                                        char *buffer, size_t buflen,
+                                        int *errnop);
 };
 
 struct authtok_conv {
@@ -145,4 +156,14 @@ errno_t get_netgroup(struct proxy_id_ctx *ctx,
                      struct sss_domain_info *dom,
                      const char *name);
 
+errno_t get_serv_byname(struct proxy_id_ctx *ctx,
+                        struct sysdb_ctx *sysdb,
+                        struct sss_domain_info *dom,
+                        const char *be_filter);
+
+errno_t get_serv_byport(struct proxy_id_ctx *ctx,
+                        struct sysdb_ctx *sysdb,
+                        struct sss_domain_info *dom,
+                        const char *be_filter);
+
 #endif /* __PROXY_H__ */
diff --git a/src/providers/proxy/proxy_id.c b/src/providers/proxy/proxy_id.c
index cfb058a8a4afd8ae2df4dfe9a21bae9dbe165668..898c0ba31b2e9c31e1446ac96b32701e288be3a0 100644
--- a/src/providers/proxy/proxy_id.c
+++ b/src/providers/proxy/proxy_id.c
@@ -1173,6 +1173,30 @@ void proxy_get_account_info(struct be_req *breq)
         ret = get_netgroup(ctx, sysdb, domain, ar->filter_value);
         break;
 
+    case BE_REQ_SERVICES:
+        switch (ar->filter_type) {
+        case BE_FILTER_NAME:
+            if (ctx->ops.getservbyname_r == NULL) {
+                return proxy_reply(breq, DP_ERR_FATAL,
+                                   ENODEV, "Services are not supported");
+            }
+            ret = get_serv_byname(ctx, sysdb, domain, ar->filter_value);
+            break;
+        case BE_FILTER_IDNUM:
+            if (ctx->ops.getservbyport_r == NULL) {
+                return proxy_reply(breq, DP_ERR_FATAL,
+                                   ENODEV, "Services are not supported");
+            }
+            ret = get_serv_byport(ctx, sysdb, domain, ar->filter_value);
+            break;
+        case BE_FILTER_ENUM:
+            break;
+        default:
+            return proxy_reply(breq, DP_ERR_FATAL,
+                               EINVAL, "Invalid filter type");
+        }
+        break;
+
     default: /*fail*/
         return proxy_reply(breq, DP_ERR_FATAL,
                            EINVAL, "Invalid request type");
diff --git a/src/providers/proxy/proxy_init.c b/src/providers/proxy/proxy_init.c
index f53d6de1c72d99c53d46c83a0c70aa829139880c..b28e65a57e92e79aee60538e9b2880540c1743f3 100644
--- a/src/providers/proxy/proxy_init.c
+++ b/src/providers/proxy/proxy_init.c
@@ -237,6 +237,28 @@ int sssm_proxy_id_init(struct be_ctx *bectx,
                                                                dlerror()));
     }
 
+    ctx->ops.getservbyname_r = proxy_dlsym(ctx->handle,
+                                           "_nss_%s_getservbyname_r",
+                                           libname);
+    if (!ctx->ops.getservbyname_r) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              ("Failed to load _nss_%s_getservbyname_r, error: %s. "
+               "The library does not support services.\n",
+               libname,
+               dlerror()));
+    }
+
+    ctx->ops.getservbyport_r = proxy_dlsym(ctx->handle,
+                                           "_nss_%s_getservbyport_r",
+                                           libname);
+    if (!ctx->ops.getservbyport_r) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              ("Failed to load _nss_%s_getservbyport_r, error: %s. "
+               "The library does not support services.\n",
+               libname,
+               dlerror()));
+    }
+
     *ops = &proxy_id_ops;
     *pvt_data = ctx;
     ret = EOK;
diff --git a/src/providers/proxy/proxy_services.c b/src/providers/proxy/proxy_services.c
new file mode 100644
index 0000000000000000000000000000000000000000..6707d81f0eb3e61bcddeb4d997ce3c44026cc0fb
--- /dev/null
+++ b/src/providers/proxy/proxy_services.c
@@ -0,0 +1,264 @@
+/*
+    SSSD
+
+    Authors:
+        Stephen Gallagher <sgallagh@redhat.com>
+
+    Copyright (C) 2012 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "providers/proxy/proxy.h"
+#include "util/util.h"
+#include "util/strtonum.h"
+#include "db/sysdb_services.h"
+
+#define BUFLEN  1024
+
+errno_t
+proxy_save_service(struct sysdb_ctx *sysdb,
+                   struct servent *svc,
+                   bool lowercase,
+                   uint64_t cache_timeout)
+{
+    errno_t ret;
+    char *cased_name;
+    const char **protocols;
+    const char **cased_aliases;
+    TALLOC_CTX *tmp_ctx;
+    size_t num_aliases, i;
+    time_t now = time(NULL);
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) return ENOMEM;
+
+    cased_name = sss_get_cased_name(tmp_ctx, svc->s_name, !lowercase);
+    if (!cased_name) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    protocols = talloc_array(tmp_ctx, const char *, 2);
+    if (!protocols) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    protocols[0] = sss_get_cased_name(protocols, svc->s_proto,
+                                      !lowercase);
+    if (!protocols[0]) {
+        ret = ENOMEM;
+        goto done;
+    }
+    protocols[1] = NULL;
+
+    /* Count the aliases */
+    for(num_aliases = 0; svc->s_aliases[num_aliases]; num_aliases++);
+    cased_aliases = talloc_array(tmp_ctx, const char *, num_aliases + 1);
+    if (!cased_aliases) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    for (i = 0; i < num_aliases; i++) {
+        cased_aliases[i] = sss_get_cased_name(tmp_ctx, svc->s_aliases[i],
+                                              !lowercase);
+        if (!cased_aliases[i]) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+    cased_aliases[num_aliases] = '\0';
+
+    ret = sysdb_store_service(sysdb,
+                              cased_name,
+                              ntohs(svc->s_port),
+                              cased_aliases,
+                              protocols,
+                              cache_timeout,
+                              now);
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+errno_t
+split_service_name_filter(TALLOC_CTX *mem_ctx,
+                          const char *filter,
+                          char **name,
+                          char **protocol)
+{
+    char *p;
+
+    *name = talloc_strdup(mem_ctx, filter);
+    if (!*name) {
+        return ENOENT;
+    }
+
+    p = strchr(*name, ':');
+    if (p) {
+        /* Protocol included */
+        *p = '\0';
+
+        *protocol = p + 1;
+    } else {
+        *protocol = NULL;
+    }
+
+    return EOK;
+}
+
+errno_t
+get_serv_byname(struct proxy_id_ctx *ctx,
+                struct sysdb_ctx *sysdb,
+                struct sss_domain_info *dom,
+                const char *be_filter)
+{
+    errno_t ret;
+    enum nss_status status;
+    struct servent *result;
+    TALLOC_CTX *tmp_ctx;
+    char *name;
+    char *protocol;
+    char buffer[BUFLEN];
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) return ENOMEM;
+
+    result = talloc_zero(tmp_ctx, struct servent);
+    if (!result) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* Split the filter */
+    ret = split_service_name_filter(tmp_ctx, be_filter, &name, &protocol);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    status = ctx->ops.getservbyname_r(name, protocol, result,
+                                      buffer, BUFLEN, &ret);
+    if (status != NSS_STATUS_SUCCESS && status != NSS_STATUS_NOTFOUND) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              ("getservbyname_r failed for service [%s].\n", be_filter));
+        return ret;
+    }
+
+    if (status == NSS_STATUS_NOTFOUND) {
+        /* Make sure we remove it from the cache */
+        ret = sysdb_svc_delete(sysdb, name, 0, protocol);
+    } else {
+
+        /* Results found. Save them into the cache */
+        ret = proxy_save_service(sysdb, result,
+                                 !dom->case_sensitive,
+                                 ctx->entry_cache_timeout);
+    }
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+errno_t
+split_service_port_filter(TALLOC_CTX *mem_ctx,
+                          const char *filter,
+                          uint16_t *port,
+                          char **protocol)
+{
+    errno_t ret;
+    char *buf;
+    char *p;
+
+    buf = talloc_strdup(NULL, filter);
+    if (!buf) {
+        return ENOENT;
+    }
+
+    p = strchr(buf, ':');
+    if (p) {
+        /* Protocol included */
+        *p = '\0';
+
+        *protocol = talloc_strdup(mem_ctx, p + 1);
+        if (!*protocol) {
+            ret = ENOMEM;
+            goto done;
+        }
+    } else {
+        *protocol = NULL;
+    }
+
+    /* The first part of the buffer is the port */
+    *port = strtouint16(buf, NULL, 0);
+    ret = errno;
+
+done:
+    talloc_free(buf);
+    return ret;
+}
+
+errno_t
+get_serv_byport(struct proxy_id_ctx *ctx,
+                struct sysdb_ctx *sysdb,
+                struct sss_domain_info *dom,
+                const char *be_filter)
+{
+    errno_t ret;
+    enum nss_status status;
+    struct servent *result;
+    TALLOC_CTX *tmp_ctx;
+    uint16_t port;
+    char *protocol;
+    char buffer[BUFLEN];
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) return ENOMEM;
+
+    result = talloc_zero(tmp_ctx, struct servent);
+    if (!result) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* Split the filter */
+    ret = split_service_port_filter(tmp_ctx, be_filter, &port, &protocol);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    status = ctx->ops.getservbyport_r(port, protocol, result,
+                                      buffer, BUFLEN, &ret);
+    if (status != NSS_STATUS_SUCCESS && status != NSS_STATUS_NOTFOUND) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              ("getservbyport_r failed for service [%s].\n", be_filter));
+        return ret;
+    }
+
+    if (status == NSS_STATUS_NOTFOUND) {
+        /* Make sure we remove it from the cache */
+        ret = sysdb_svc_delete(sysdb, NULL, port, protocol);
+    } else {
+        /* Results found. Save them into the cache */
+        ret = proxy_save_service(sysdb, result,
+                                 !dom->case_sensitive,
+                                 ctx->entry_cache_timeout);
+    }
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
-- 
1.7.7.5

