From 5ff297c7d63c9e128af5fbca2538b728809c4561 Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Mon, 23 Aug 2010 16:05:47 -0400
Subject: [PATCH 07/10] Add support for netgroups to NSS sss_client

---
 Makefile.am                    |    4 +-
 src/sss_client/nss_compat.h    |   67 +++++++++
 src/sss_client/nss_netgroup.c  |  293 ++++++++++++++++++++++++++++++++++++++++
 src/sss_client/sss_cli.h       |    4 +-
 src/sss_client/sss_nss.exports |    6 +-
 5 files changed, 368 insertions(+), 6 deletions(-)
 create mode 100644 src/sss_client/nss_compat.h
 create mode 100644 src/sss_client/nss_netgroup.c

diff --git a/Makefile.am b/Makefile.am
index fd795af0246f22c53f150737a414b119d68206ac..7c8c847672f2eba6ced39bcd231009ada97fecd1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -727,7 +727,9 @@ libnss_sss_la_SOURCES = \
     src/sss_client/common.c \
     src/sss_client/nss_passwd.c \
     src/sss_client/nss_group.c \
-    src/sss_client/sss_cli.h
+    src/sss_client/nss_netgroup.c \
+    src/sss_client/sss_cli.h \
+    src/sss_client/nss_compat.h
 libnss_sss_la_LDFLAGS = \
     -module \
     -version-info 2:0:0 \
diff --git a/src/sss_client/nss_compat.h b/src/sss_client/nss_compat.h
new file mode 100644
index 0000000000000000000000000000000000000000..37871c230c0080628c9f5617b6efd2fbddde427d
--- /dev/null
+++ b/src/sss_client/nss_compat.h
@@ -0,0 +1,67 @@
+/*
+    SSSD
+
+    nss_compat.h
+
+    Authors:
+        Stephen Gallagher <sgallagh@redhat.com>
+
+    Copyright (C) 2010 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/>.
+
+    Portions of this source file were copied from nss-pam-ldapd version
+    0.7.8, licensed under LGPLv2.1+
+*/
+
+#ifndef NSS_COMPAT_H_
+#define NSS_COMPAT_H_
+
+/* We also define struct __netgrent because it's definition is
+   not publically available. This is taken from inet/netgroup.h
+   of the glibc (2.3.6) source tarball.
+   The first part of the struct is the only part that is modified
+   by our getnetgrent() function, all the other fields are not
+   touched at all. */
+struct __netgrent
+{
+  enum { triple_val, group_val } type;
+  union
+  {
+    struct
+    {
+      const char *host;
+      const char *user;
+      const char *domain;
+    } triple;
+    const char *group;
+  } val;
+  /* the following stuff is used by some NSS services
+     but not by ours (it's not completely clear how these
+     are shared between different services) or is used
+     by our caller */
+  char *data;
+  size_t data_size;
+  union
+  {
+    char *cursor;
+    unsigned long int position;
+  } insertedname; /* added name to union to avoid warning */
+  int first;
+  struct name_list *known_groups;
+  struct name_list *needed_groups;
+  void *nip; /* changed from `service_user *nip' */
+};
+
+#endif /* NSS_COMPAT_H_ */
diff --git a/src/sss_client/nss_netgroup.c b/src/sss_client/nss_netgroup.c
new file mode 100644
index 0000000000000000000000000000000000000000..bdbd87667ee2d6abd6b64d48547f95ba5c3e572c
--- /dev/null
+++ b/src/sss_client/nss_netgroup.c
@@ -0,0 +1,293 @@
+/*
+    SSSD
+
+    nss_netgroup.c
+
+    Authors:
+        Stephen Gallagher <sgallagh@redhat.com>
+
+    Copyright (C) 2010 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 <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"
+#include "protos.h"
+#include "nss_compat.h"
+
+#define MAX_NETGR_NAME_LENGTH 2048
+
+static struct sss_nss_getnetgrent_data {
+    char *name;
+    size_t len;
+    size_t ptr;
+    uint8_t *data;
+} sss_nss_getnetgrent_data;
+
+/*
+ * Replies:
+ *
+ * 0-3: 32bit unsigned number of results
+ * 4-7: 32bit unsigned (reserved/padding)
+ *  For each result:
+ *  8-X: sequence of \0 terminated strings representing tuple
+ *       (host, user, domain)
+ */
+#define NETGR_METADATA_COUNT 2 * sizeof(uint32_t)
+struct sss_nss_netgr_rep {
+    struct __netgrent *result;
+    char *buffer;
+    size_t buflen;
+};
+
+static void sss_nss_getnetgrent_data_clean(void) {
+    if (sss_nss_getnetgrent_data.name != NULL) {
+        free(sss_nss_getnetgrent_data.name);
+        sss_nss_getnetgrent_data.name = NULL;
+    }
+    if (sss_nss_getnetgrent_data.data != NULL) {
+        free(sss_nss_getnetgrent_data.data);
+        sss_nss_getnetgrent_data.data = NULL;
+    }
+    sss_nss_getnetgrent_data.len = 0;
+    sss_nss_getnetgrent_data.ptr = 0;
+}
+
+static int sss_nss_getnetgr_readrep(struct sss_nss_netgr_rep *pr,
+                                    uint8_t *buf, size_t *len)
+{
+    char *sbuf;
+    size_t i, slen;
+    ssize_t dlen;
+
+    if (*len < 3) {
+        /* Not enough space for data, bad packet */
+        return EBADMSG;
+    }
+
+    sbuf = (char *)&buf[0];
+    slen = *len;
+    dlen = pr->buflen;
+
+    /* Host value */
+    i = 0;
+    pr->result->val.triple.host = &(pr->buffer[i]);
+    while (slen > i && dlen > 0) {
+        pr->buffer[i] = sbuf[i];
+        if (pr->buffer[i] == '\0') break;
+        i++;
+        dlen--;
+    }
+    if (slen <= i) { /* premature end of buf */
+        return EBADMSG;
+    }
+    if (dlen <= 0) { /* not enough memory */
+        return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
+    }
+    i++;
+    dlen--;
+
+    /* libc expects NULL instead of empty string */
+    if (strlen(pr->result->val.triple.host) == 0) {
+        pr->result->val.triple.host = NULL;
+    }
+
+    /* User value */
+    pr->result->val.triple.user = &(pr->buffer[i]);
+    while (slen > i && dlen > 0) {
+        pr->buffer[i] = sbuf[i];
+        if (pr->buffer[i] == '\0') break;
+        i++;
+        dlen--;
+    }
+    if (slen <= i) { /* premature end of buf */
+        return EBADMSG;
+    }
+    if (dlen <= 0) { /* not enough memory */
+        return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
+    }
+    i++;
+    dlen--;
+
+    /* libc expects NULL instead of empty string */
+    if (strlen(pr->result->val.triple.user) == 0) {
+        pr->result->val.triple.user = NULL;
+    }
+
+    /* Domain value */
+    pr->result->val.triple.domain = &(pr->buffer[i]);
+    while (slen > i && dlen > 0) {
+        pr->buffer[i] = sbuf[i];
+        if (pr->buffer[i] == '\0') break;
+        i++;
+        dlen--;
+    }
+    if (slen <= i) { /* premature end of buf */
+        return EBADMSG;
+    }
+    if (dlen <= 0) { /* not enough memory */
+        return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
+    }
+    i++;
+    dlen--;
+
+    /* libc expects NULL instead of empty string */
+    if (strlen(pr->result->val.triple.domain) == 0) {
+        pr->result->val.triple.domain = NULL;
+    }
+
+    *len = slen -i;
+
+    return 0;
+}
+
+enum nss_status _nss_sss_setnetgrent(const char *netgroup,
+                     struct __netgrent *result)
+{
+    uint8_t *repbuf = NULL;
+    size_t replen;
+    enum nss_status nret;
+    struct sss_cli_req_data rd;
+    int errnop;
+    size_t name_len;
+    errno_t ret;
+
+    if (!netgroup) return NSS_STATUS_NOTFOUND;
+
+    /* make sure we do not have leftovers, and release memory */
+    sss_nss_getnetgrent_data_clean();
+
+    ret = sss_strnlen(netgroup, MAX_NETGR_NAME_LENGTH, &name_len);
+    if (ret != 0) return NSS_STATUS_NOTFOUND;
+
+    sss_nss_getnetgrent_data.name = malloc(sizeof(char)*name_len + 1);
+    if (sss_nss_getnetgrent_data.name == NULL) {
+        return NSS_STATUS_TRYAGAIN;
+    }
+    strncpy(sss_nss_getnetgrent_data.name, netgroup, name_len + 1);
+
+    rd.data = sss_nss_getnetgrent_data.name;
+    rd.len = name_len + 1;
+
+    nret = sss_nss_make_request(SSS_NSS_SETNETGRENT, &rd,
+                                &repbuf, &replen, &errnop);
+    if (nret != NSS_STATUS_SUCCESS) {
+        errno = errnop;
+        return nret;
+    }
+
+    /* no results if not found */
+    if ((((uint32_t *)repbuf)[0] == 0) || (replen < NETGR_METADATA_COUNT)) {
+        free(repbuf);
+        return NSS_STATUS_NOTFOUND;
+    }
+
+    return NSS_STATUS_SUCCESS;
+}
+
+enum nss_status _nss_sss_getnetgrent_r(struct __netgrent *result,
+                       char *buffer, size_t buflen,
+                       int *errnop)
+{
+    struct sss_cli_req_data rd;
+    struct sss_nss_netgr_rep netgrrep;
+    uint8_t *repbuf;
+    size_t replen;
+    enum nss_status nret;
+    uint32_t num_entries;
+    int ret;
+
+    /* Caught once glibc passing in buffer == 0x0 */
+    if (!buffer || !buflen) return ERANGE;
+
+    /* If we're already processing result data, continue to
+     * return it.
+     */
+    if (sss_nss_getnetgrent_data.data != NULL &&
+        sss_nss_getnetgrent_data.ptr < sss_nss_getnetgrent_data.len) {
+
+        repbuf = (uint8_t *)sss_nss_getnetgrent_data.data +
+                sss_nss_getnetgrent_data.ptr;
+        replen = sss_nss_getnetgrent_data.len -
+                    sss_nss_getnetgrent_data.ptr;
+
+        netgrrep.result = result;
+        netgrrep.buffer = buffer;
+        netgrrep.buflen = buflen;
+
+        ret = sss_nss_getnetgr_readrep(&netgrrep, repbuf, &replen);
+        if (ret != 0) {
+            *errnop = ret;
+            return NSS_STATUS_TRYAGAIN;
+        }
+
+        sss_nss_getnetgrent_data.ptr = sss_nss_getnetgrent_data.len - replen;
+
+        return NSS_STATUS_SUCCESS;
+    }
+
+    /* Release memory, if any */
+    sss_nss_getnetgrent_data_clean();
+
+    /* retrieve no more than SSS_NSS_MAX_ENTRIES at a time */
+    num_entries = SSS_NSS_MAX_ENTRIES;
+    rd.len = sizeof(uint32_t);
+    rd.data = &num_entries;
+
+    nret = sss_nss_make_request(SSS_NSS_GETNETGRENT, &rd,
+                                &repbuf, &replen, errnop);
+    if (nret != NSS_STATUS_SUCCESS) {
+        return nret;
+    }
+
+    /* no results if not found */
+    if ((((uint32_t *)repbuf)[0] == 0) || (replen <= NETGR_METADATA_COUNT)) {
+        free(repbuf);
+        return NSS_STATUS_NOTFOUND;
+    }
+
+    sss_nss_getnetgrent_data.data = repbuf;
+    sss_nss_getnetgrent_data.len = replen;
+    /* skip metadata fields */
+    sss_nss_getnetgrent_data.ptr = NETGR_METADATA_COUNT;
+
+    /* call again ourselves, this will return the first result */
+    return _nss_sss_getnetgrent_r(result, buffer, buflen, errnop);
+}
+
+enum nss_status _nss_sss_endnetgrent(void)
+{
+    enum nss_status nret;
+    int errnop;
+
+    /* make sure we do not have leftovers, and release memory */
+    sss_nss_getnetgrent_data_clean();
+
+    nret = sss_nss_make_request(SSS_NSS_ENDNETGRENT,
+                                NULL, NULL, NULL, &errnop);
+    if (nret != NSS_STATUS_SUCCESS) {
+        errno = errnop;
+        return nret;
+    }
+
+    return NSS_STATUS_SUCCESS;
+}
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index e0a33df46d45358646fd8f32237cc1a176dd391f..1a068d5f3f46e14107713f079c8f13920ad6ab7f 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -93,14 +93,14 @@ enum sss_cli_command {
     SSS_NSS_SETHOSTENT     = 0x0054,
     SSS_NSS_GETHOSTENT     = 0x0055,
     SSS_NSS_ENDHOSTENT     = 0x0056,
-
+#endif
 /* netgroup */
 
     SSS_NSS_SETNETGRENT    = 0x0061,
     SSS_NSS_GETNETGRENT    = 0x0062,
     SSS_NSS_ENDNETGRENT    = 0x0063,
     /* SSS_NSS_INNETGR     = 0x0064, */
-
+#if 0
 /* networks */
 
     SSS_NSS_GETNETBYNAME   = 0x0071,
diff --git a/src/sss_client/sss_nss.exports b/src/sss_client/sss_nss.exports
index bcc6b10e17849efe146f2a935079834dcbabc363..2fa4e5f2f27c51dd6cfb61c23b5d8bc0452a8453 100644
--- a/src/sss_client/sss_nss.exports
+++ b/src/sss_client/sss_nss.exports
@@ -34,9 +34,9 @@ EXPORTED {
 		#_nss_sss_gethostent_r;
 		#_nss_sss_endhostent;
 
-		#_nss_sss_setnetgrent;
-		#_nss_sss_getnetgrent_r;
-		#_nss_sss_endnetgrent;
+		_nss_sss_setnetgrent;
+		_nss_sss_getnetgrent_r;
+		_nss_sss_endnetgrent;
 
 		#_nss_sss_getnetbyname_r;
 		#_nss_sss_getnetbyaddr_r;
-- 
1.7.2.3

