>From 9a9994f907b34cb4c90542c26b005bab7e9bf612 Mon Sep 17 00:00:00 2001
From: Simo Sorce <simo@redhat.com>
Date: Tue, 21 Feb 2012 12:38:04 -0500
Subject: [PATCH] nss_group: Cache the result from sssd when the glibc
 provided buffer is too small.

---
 src/sss_client/nss_group.c |  144 +++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 136 insertions(+), 8 deletions(-)

diff --git a/src/sss_client/nss_group.c b/src/sss_client/nss_group.c
index 7e5f79ad5d17d1065fdbd647ed97331e37e18d80..49d5d05051c0878b864cb460685f7935690b4685 100644
--- a/src/sss_client/nss_group.c
+++ b/src/sss_client/nss_group.c
@@ -27,6 +27,7 @@
 #include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
+#include <stdbool.h>
 #include "sss_cli.h"
 
 static struct sss_nss_getgrent_data {
@@ -35,8 +36,8 @@ static struct sss_nss_getgrent_data {
     uint8_t *data;
 } sss_nss_getgrent_data;
 
-static void sss_nss_getgrent_data_clean(void) {
-
+static void sss_nss_getgrent_data_clean(void)
+{
     if (sss_nss_getgrent_data.data != NULL) {
         free(sss_nss_getgrent_data.data);
         sss_nss_getgrent_data.data = NULL;
@@ -45,6 +46,117 @@ static void sss_nss_getgrent_data_clean(void) {
     sss_nss_getgrent_data.ptr = 0;
 }
 
+enum sss_nss_gr_type {
+    GETGR_NONE,
+    GETGR_NAME,
+    GETGR_GID
+};
+
+static struct sss_nss_getgr_data {
+    enum sss_nss_gr_type type;
+    union {
+        char *grname;
+        gid_t gid;
+    } id;
+
+    uint8_t *repbuf;
+    size_t replen;
+} sss_nss_getgr_data;
+
+static void sss_nss_getgr_data_clean(bool freebuf)
+{
+    if (sss_nss_getgr_data.type == GETGR_NAME) {
+        free(sss_nss_getgr_data.id.grname);
+    }
+    if (freebuf) {
+        free(sss_nss_getgr_data.repbuf);
+    }
+    memset(&sss_nss_getgr_data, 0, sizeof(struct sss_nss_getgr_data));
+}
+
+static enum nss_status sss_nss_get_getgr_cache(const char *name, gid_t gid,
+                                               enum sss_nss_gr_type type,
+                                               uint8_t **repbuf,
+                                               size_t *replen,
+                                               int *errnop)
+{
+    bool freebuf = true;
+    enum nss_status status;
+    int ret = 0;
+
+    if (sss_nss_getgr_data.type != type) {
+        status = NSS_STATUS_NOTFOUND;
+        goto done;
+    }
+
+    switch (type) {
+    case GETGR_NAME:
+        ret = strcmp(name, sss_nss_getgr_data.id.grname);
+        if (ret != 0) {
+            status = NSS_STATUS_NOTFOUND;
+            goto done;
+        }
+        break;
+    case GETGR_GID:
+        if (sss_nss_getgr_data.id.gid != gid) {
+            status = NSS_STATUS_NOTFOUND;
+            goto done;
+        }
+        break;
+    default:
+        status = NSS_STATUS_TRYAGAIN;
+        ret = EINVAL;
+        goto done;
+    }
+
+    /* ok we have it, remove from cache and pass back to the caller */
+    *repbuf = sss_nss_getgr_data.repbuf;
+    *replen = sss_nss_getgr_data.replen;
+
+    /* prevent _clean() from freeing the buffer */
+    freebuf = false;
+
+done:
+    sss_nss_getgr_data_clean(freebuf);
+    *errnop = ret;
+    return status;
+}
+
+/* this function always takes ownership of repbuf and NULLs it before
+ * returning */
+static void sss_nss_save_getgr_cache(const char *name, gid_t gid,
+                                     enum sss_nss_gr_type type,
+                                     uint8_t **repbuf, size_t replen)
+{
+    int ret = 0;
+
+    sss_nss_getgr_data.type = type;
+    sss_nss_getgr_data.repbuf = *repbuf;
+    sss_nss_getgr_data.replen = replen;
+
+    switch (type) {
+    case GETGR_NAME:
+        sss_nss_getgr_data.id.grname = strdup(name);
+        if (!sss_nss_getgr_data.id.grname) {
+            ret = ENOMEM;
+            goto done;
+        }
+        break;
+    case GETGR_GID:
+        sss_nss_getgr_data.id.gid = gid;
+        break;
+    default:
+        ret = EINVAL;
+        goto done;
+    }
+
+done:
+    if (ret) {
+        sss_nss_getgr_data_clean(true);
+    }
+    *repbuf = NULL;
+}
+
 /* GETGRNAM Request:
  *
  * 0-X: string with name
@@ -249,8 +361,12 @@ enum nss_status _nss_sss_getgrnam_r(const char *name, struct group *result,
 
     sss_nss_lock();
 
-    nret = sss_nss_make_request(SSS_NSS_GETGRNAM, &rd,
-                                &repbuf, &replen, errnop);
+    nret = sss_nss_get_getgr_cache(name, 0, GETGR_NAME,
+                                   &repbuf, &replen, errnop);
+    if (nret == NSS_STATUS_NOTFOUND) {
+        nret = sss_nss_make_request(SSS_NSS_GETGRNAM, &rd,
+                                    &repbuf, &replen, errnop);
+    }
     if (nret != NSS_STATUS_SUCCESS) {
         goto out;
     }
@@ -276,7 +392,11 @@ enum nss_status _nss_sss_getgrnam_r(const char *name, struct group *result,
 
     len = replen - 8;
     ret = sss_nss_getgr_readrep(&grrep, repbuf+8, &len);
-    free(repbuf);
+    if (ret == ERANGE) {
+        sss_nss_save_getgr_cache(name, 0, GETGR_NAME, &repbuf, replen);
+    } else {
+        free(repbuf);
+    }
     if (ret) {
         *errnop = ret;
         nret = NSS_STATUS_TRYAGAIN;
@@ -310,8 +430,12 @@ enum nss_status _nss_sss_getgrgid_r(gid_t gid, struct group *result,
 
     sss_nss_lock();
 
-    nret = sss_nss_make_request(SSS_NSS_GETGRGID, &rd,
-                                &repbuf, &replen, errnop);
+    nret = sss_nss_get_getgr_cache(NULL, gid, GETGR_GID,
+                                   &repbuf, &replen, errnop);
+    if (nret == NSS_STATUS_NOTFOUND) {
+        nret = sss_nss_make_request(SSS_NSS_GETGRGID, &rd,
+                                    &repbuf, &replen, errnop);
+    }
     if (nret != NSS_STATUS_SUCCESS) {
         goto out;
     }
@@ -337,7 +461,11 @@ enum nss_status _nss_sss_getgrgid_r(gid_t gid, struct group *result,
 
     len = replen - 8;
     ret = sss_nss_getgr_readrep(&grrep, repbuf+8, &len);
-    free(repbuf);
+    if (ret == ERANGE) {
+        sss_nss_save_getgr_cache(NULL, gid, GETGR_GID, &repbuf, replen);
+    } else {
+        free(repbuf);
+    }
     if (ret) {
         *errnop = ret;
         nret = NSS_STATUS_TRYAGAIN;
-- 
1.7.7.6

