From 04d8989c30682d6dbfd99d77a47023e579d4bc52 Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Fri, 6 Jan 2012 13:57:30 -0500
Subject: [PATCH 03/12] NSS: Add client support for services (non-enumeration)

---
 Makefile.am                    |    1 +
 src/sss_client/nss_services.c  |  357 ++++++++++++++++++++++++++++++++++++++++
 src/sss_client/sss_cli.h       |   17 ++
 src/sss_client/sss_nss.exports |   10 +-
 4 files changed, 380 insertions(+), 5 deletions(-)
 create mode 100644 src/sss_client/nss_services.c

diff --git a/Makefile.am b/Makefile.am
index 93c5ff56a3e7b9ba4aae1a7ccd4be52d35c02820..76aa91c2d4121ab2f529363e454a2f26f972f1f2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -849,6 +849,7 @@ libnss_sss_la_SOURCES = \
     src/sss_client/nss_passwd.c \
     src/sss_client/nss_group.c \
     src/sss_client/nss_netgroup.c \
+    src/sss_client/nss_services.c \
     src/sss_client/sss_cli.h \
     src/sss_client/nss_compat.h
 libnss_sss_la_LDFLAGS = \
diff --git a/src/sss_client/nss_services.c b/src/sss_client/nss_services.c
new file mode 100644
index 0000000000000000000000000000000000000000..1db9b76c3c033ce67431dc1dc3377e47b40d5c10
--- /dev/null
+++ b/src/sss_client/nss_services.c
@@ -0,0 +1,357 @@
+/*
+    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 <nss.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include "sss_cli.h"
+
+
+/* GETSERVBYNAME Request
+ *
+ * 0-X: Sequence of two, zero-terminated strings (name, protocol).
+ * Protocol may be zero-length to imply "any"
+ *
+ * GETSERVBYPORT Request:
+ * 0-1: 16-bit port number in network byte order
+ * 2-7: Reserved/padding
+ * 8-X: Zero-terminated string (protocol)
+ * Protocol may be zero-length to imply "any"
+ *
+ * Replies:
+ * 0-3: 32-bit unsigned number of results
+ * 4-7: 32-bit unsigned (reserved/padding)
+ * 7-X: Result data (blocks equal to number of results)
+ *
+ * Result data:
+ * 0-1: 16-bit unsigned port number in network byte order
+ * 2-3: padding
+ * 4-7: 32-bit unsigned number of aliases
+ * 8-X: sequence of zero-terminated strings
+ *      (name, protocol, zero or more aliases)
+ */
+struct sss_nss_svc_rep {
+    struct servent *result;
+    char *buffer;
+    size_t buflen;
+};
+
+static errno_t
+sss_nss_getsvc_readrep(struct sss_nss_svc_rep *sr,
+                       uint8_t *buf, size_t *len)
+{
+    errno_t ret;
+    uint16_t c;
+    uint32_t num_aliases;
+    size_t i, l, slen, dlen, pad, ptaliases, alen;
+    char *sbuf;
+
+    /* Buffer must contain two 32-bit integers,
+     * at least one character and null-terminator
+     * for the name, and at least a null-
+     * terminator for the protocol.
+     */
+    if (*len < 11) {
+        /* not enough space for data, bad packet */
+        return EBADMSG;
+    }
+
+    /* Get the port */
+    SAFEALIGN_COPY_UINT16(&c, buf, NULL);
+    sr->result->s_port = c;
+
+    /* Get the number of aliases */
+    SAFEALIGN_COPY_UINT32(&c, buf + sizeof(uint32_t), NULL);
+    num_aliases = c;
+
+    sbuf = (char *)&buf[2 * sizeof(uint32_t)];
+    slen = *len - (2 * sizeof(uint32_t));
+    dlen = sr->buflen;
+
+    /* Copy in the name */
+    i = 0;
+    sr->result->s_name = &(sr->buffer[i]);
+
+    ret = sss_readrep_copy_string(sbuf, &i,
+                                  &slen, &dlen,
+                                  &sr->result->s_name,
+                                  NULL);
+    if (ret != EOK) return ret;
+
+    /* Copy in the protocol */
+    sr->result->s_proto = &(sr->buffer[i]);
+
+    ret = sss_readrep_copy_string(sbuf, &i,
+                                  &slen, &dlen,
+                                  &sr->result->s_proto,
+                                  NULL);
+    if (ret != EOK) return ret;
+
+    /* Make sure sr->buffer[i+pad] is 32-bit aligned */
+    pad = 0;
+    while((i + pad) % 4) {
+        pad++;
+    }
+
+    /* Copy in the aliases */
+    sr->result->s_aliases = (char **) &(sr->buffer[i+pad]);
+
+    ptaliases = (sizeof(char *) * (num_aliases + 1)) + pad;
+    if (ptaliases > dlen) {
+        return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
+    }
+
+    dlen -= ptaliases;
+    ptaliases += i;
+    sr->result->s_aliases[num_aliases] = NULL; /* terminate array */
+
+    for (l = 0; l < num_aliases; l++) {
+        sr->result->s_aliases[l] = &(sr->buffer[ptaliases]);
+        ret = sss_readrep_copy_string(sbuf, &i,
+                                      &slen, &dlen,
+                                      &sr->result->s_aliases[l],
+                                      &alen);
+        if (ret != EOK) return ret;
+
+        ptaliases += alen + 1;
+    }
+
+    *len = slen - i;
+
+    return EOK;
+}
+
+enum nss_status
+_nss_sss_getservbyname_r(const char *name,
+                         const char *protocol,
+                         struct servent *result,
+                         char *buffer, size_t buflen,
+                         int *errnop)
+{
+    struct sss_cli_req_data rd;
+    struct sss_nss_svc_rep svcrep;
+    size_t name_len;
+    size_t proto_len = 0;
+    uint8_t *repbuf;
+    uint8_t *data;
+    size_t replen, len;
+    enum nss_status nret;
+    int ret;
+
+    /* Caught once glibc passing in buffer == 0x0 */
+    if (!buffer || !buflen) return ERANGE;
+
+    ret = sss_strnlen(name, SSS_NAME_MAX, &name_len);
+    if (ret != 0) {
+        *errnop = EINVAL;
+        return NSS_STATUS_NOTFOUND;
+    }
+
+    if (protocol) {
+        ret = sss_strnlen(protocol, SSS_NAME_MAX, &proto_len);
+        if (ret != 0) {
+            *errnop = EINVAL;
+            return NSS_STATUS_NOTFOUND;
+        }
+    }
+
+    rd.len = name_len + proto_len + 2;
+    data = malloc(sizeof(uint8_t)*rd.len);
+    if (data == NULL) {
+        nret = NSS_STATUS_TRYAGAIN;
+        goto out;
+    }
+
+    memcpy(data, name, name_len + 1);
+
+    if (protocol) {
+        memcpy(data + name_len + 1, protocol, proto_len + 1);
+    } else {
+        /* No protocol specified, pass empty string */
+        data[name_len + 1] = '\0';
+    }
+    rd.data = data;
+
+    sss_nss_lock();
+
+    nret = sss_nss_make_request(SSS_NSS_GETSERVBYNAME, &rd,
+                                &repbuf, &replen, errnop);
+    free(data);
+    if (nret != NSS_STATUS_SUCCESS) {
+        goto out;
+    }
+
+    svcrep.result = result;
+    svcrep.buffer = buffer;
+    svcrep.buflen = buflen;
+
+    /* no results if not found */
+    if (((uint32_t *)repbuf)[0] == 0) {
+        free(repbuf);
+        nret = NSS_STATUS_NOTFOUND;
+        goto out;
+    }
+
+    /* only 1 result is accepted for this function */
+    if (((uint32_t *)repbuf)[0] != 1) {
+        *errnop = EBADMSG;
+        free(repbuf);
+        nret = NSS_STATUS_TRYAGAIN;
+        goto out;
+    }
+
+    len = replen - 8;
+    ret = sss_nss_getsvc_readrep(&svcrep, repbuf+8, &len);
+    free(repbuf);
+    if (ret) {
+        *errnop = ret;
+        nret = NSS_STATUS_TRYAGAIN;
+        goto out;
+    }
+
+    nret = NSS_STATUS_SUCCESS;
+
+out:
+    sss_nss_unlock();
+    return nret;
+}
+
+
+enum nss_status
+_nss_sss_getservbyport_r(int port, const char *protocol,
+                         struct servent *result,
+                         char *buffer, size_t buflen,
+                         int *errnop)
+{
+    struct sss_cli_req_data rd;
+    struct sss_nss_svc_rep svcrep;
+    size_t proto_len = 0;
+    uint8_t *repbuf;
+    uint8_t *data;
+    size_t replen, len;
+    enum nss_status nret;
+    int ret;
+
+    /* Caught once glibc passing in buffer == 0x0 */
+    if (!buffer || !buflen) return ERANGE;
+
+    if (protocol) {
+        ret = sss_strnlen(protocol, SSS_NAME_MAX, &proto_len);
+        if (ret != 0) {
+            *errnop = EINVAL;
+            return NSS_STATUS_NOTFOUND;
+        }
+    }
+
+    rd.len = sizeof(uint32_t)*2 + proto_len + 1;
+    data = malloc(sizeof(char)*rd.len);
+    if (data == NULL) {
+        nret = NSS_STATUS_TRYAGAIN;
+        goto out;
+    }
+
+    SAFEALIGN_SET_UINT16(data, port, NULL);
+
+    /* Pad the next six bytes */
+    memset(data + sizeof(uint16_t), 0, 6);
+
+    if (protocol) {
+        memcpy(data + sizeof(uint32_t)*2, protocol, proto_len + 1);
+    } else {
+        /* No protocol specified, pass empty string */
+        data[sizeof(uint32_t)*2] = '\0';
+    }
+    rd.data = data;
+
+    sss_nss_lock();
+
+    nret = sss_nss_make_request(SSS_NSS_GETSERVBYPORT, &rd,
+                                &repbuf, &replen, errnop);
+    free(data);
+    if (nret != NSS_STATUS_SUCCESS) {
+        goto out;
+    }
+
+    svcrep.result = result;
+    svcrep.buffer = buffer;
+    svcrep.buflen = buflen;
+
+    /* no results if not found */
+    if (((uint32_t *)repbuf)[0] == 0) {
+        free(repbuf);
+        nret = NSS_STATUS_NOTFOUND;
+        goto out;
+    }
+
+    /* only 1 result is accepted for this function */
+    if (((uint32_t *)repbuf)[0] != 1) {
+        *errnop = EBADMSG;
+        free(repbuf);
+        nret = NSS_STATUS_TRYAGAIN;
+        goto out;
+    }
+
+    len = replen - 8;
+    ret = sss_nss_getsvc_readrep(&svcrep, repbuf+8, &len);
+    free(repbuf);
+    if (ret) {
+        *errnop = ret;
+        nret = NSS_STATUS_TRYAGAIN;
+        goto out;
+    }
+
+    nret = NSS_STATUS_SUCCESS;
+
+out:
+    sss_nss_unlock();
+    return nret;
+}
+
+
+enum nss_status
+_nss_sss_setservent(void)
+{
+    return NSS_STATUS_UNAVAIL;
+}
+
+
+enum nss_status
+_nss_sss_getservent_r(struct servent *result,
+                      char *buffer, size_t buflen,
+                      int *errnop)
+{
+    fprintf(stderr, "getservent [%s]\n", buffer);
+    return NSS_STATUS_UNAVAIL;
+}
+
+
+enum nss_status
+_nss_sss_endservent(void)
+{
+    return NSS_STATUS_UNAVAIL;
+}
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index c6e53e60af3cca68fac6d7b3934584a036870901..c17e3765c9a6ba4bd8ce0e77e8e8ba824a8391cc 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -138,6 +138,7 @@ enum sss_cli_command {
     SSS_NSS_SETRPCENT      = 0x0093,
     SSS_NSS_GETRPCENT      = 0x0094,
     SSS_NSS_ENDRPCENT      = 0x0095,
+#endif
 
 /* services */
 
@@ -147,6 +148,7 @@ enum sss_cli_command {
     SSS_NSS_GETSERVENT     = 0x00A4,
     SSS_NSS_ENDSERVENT     = 0x00A5,
 
+#if 0
 /* shadow */
 
     SSS_NSS_GETSPNAM       = 0x00B1,
@@ -476,10 +478,25 @@ safealign_memcpy(void *dest, const void *src, size_t n, size_t *counter)
     }
 }
 
+#define SAFEALIGN_SET_VALUE(dest, value, type, pctr) do { \
+    type CV_MACRO_val = (type)(value); \
+    safealign_memcpy(dest, &CV_MACRO_val, sizeof(type), pctr); \
+} while(0)
+
 #define SAFEALIGN_COPY_UINT32(dest, src, pctr) \
     safealign_memcpy(dest, src, sizeof(uint32_t), pctr)
 #endif
 
+#ifndef SAFEALIGN_SET_UINT16
+#define SAFEALIGN_SET_UINT16(dest, value, pctr) \
+    SAFEALIGN_SET_VALUE(dest, value, uint16_t, pctr)
+#endif
+
+#ifndef SAFEALIGN_COPY_UINT16
+#define SAFEALIGN_COPY_UINT16(dest, src, pctr) \
+    safealign_memcpy(dest, src, sizeof(uint16_t), pctr)
+#endif
+
 #if 0
 
 /* GETSPNAM Request:
diff --git a/src/sss_client/sss_nss.exports b/src/sss_client/sss_nss.exports
index 2fa4e5f2f27c51dd6cfb61c23b5d8bc0452a8453..1eefea8d58523538f3f599afb965c0d754730fad 100644
--- a/src/sss_client/sss_nss.exports
+++ b/src/sss_client/sss_nss.exports
@@ -56,11 +56,11 @@ EXPORTED {
 		#_nss_sss_getrpcent_r;
 		#_nss_sss_endrpcent;
 
-		#_nss_sss_getservbyname_r;
-		#_nss_sss_getservbyport_r;
-		#_nss_sss_setservent;
-		#_nss_sss_getservent_r;
-		#_nss_sss_endservent;
+		_nss_sss_getservbyname_r;
+		_nss_sss_getservbyport_r;
+		_nss_sss_setservent;
+		_nss_sss_getservent_r;
+		_nss_sss_endservent;
 
 		#_nss_sss_getspnam_r;
 		#_nss_sss_setspent;
-- 
1.7.7.5

