>From 2b0b0fed2a09cadf372efa102d2f518394af72d2 Mon Sep 17 00:00:00 2001
From: Simo Sorce <ssorce@redhat.com>
Date: Fri, 14 Aug 2009 13:51:19 -0400
Subject: [PATCH 2/3] Fix group replies when using member/memberof

Also remove legacy memberuid support
---
 server/responder/common/responder_packet.c |   45 ++++-
 server/responder/common/responder_packet.h |    2 +
 server/responder/nss/nsssrv_cmd.c          |  330 ++++++++++++----------------
 3 files changed, 180 insertions(+), 197 deletions(-)

diff --git a/server/responder/common/responder_packet.c b/server/responder/common/responder_packet.c
index 2476293..6e581a3 100644
--- a/server/responder/common/responder_packet.c
+++ b/server/responder/common/responder_packet.c
@@ -119,12 +119,16 @@ int sss_packet_grow(struct sss_packet *packet, size_t size)
         }
 
         packet->memsize = totlen;
-        packet->buffer = newmem;
-        packet->len = &((uint32_t *)packet->buffer)[0];
-        packet->cmd = &((uint32_t *)packet->buffer)[1];
-        packet->status = &((uint32_t *)packet->buffer)[2];
-        packet->reserved = &((uint32_t *)packet->buffer)[3];
-        packet->body = (uint8_t *)&((uint32_t *)packet->buffer)[4];
+
+        /* re-set pointers if realloc had to move memory */
+        if (newmem != packet->buffer) {
+            packet->buffer = newmem;
+            packet->len = &((uint32_t *)packet->buffer)[0];
+            packet->cmd = &((uint32_t *)packet->buffer)[1];
+            packet->status = &((uint32_t *)packet->buffer)[2];
+            packet->reserved = &((uint32_t *)packet->buffer)[3];
+            packet->body = (uint8_t *)&((uint32_t *)packet->buffer)[4];
+        }
     }
 
     *(packet->len) += size;
@@ -132,6 +136,35 @@ int sss_packet_grow(struct sss_packet *packet, size_t size)
     return 0;
 }
 
+/* reclaim backet previously resrved space in the packet
+ * usually done in functione recovering from not fatal erros */
+int sss_packet_shrink(struct sss_packet *packet, size_t size)
+{
+    size_t newlen;
+
+    if (size > *(packet->len)) return EINVAL;
+
+    newlen = *(packet->len) - size;
+    if (newlen < SSS_NSS_HEADER_SIZE) return EINVAL;
+
+    *(packet->len) = newlen;
+    return 0;
+}
+
+int sss_packet_set_size(struct sss_packet *packet, size_t size)
+{
+    size_t newlen;
+
+    newlen = SSS_NSS_HEADER_SIZE + size;
+
+    /* make sure we do not overflow */
+    if (packet->memsize < newlen) return EINVAL;
+
+    *(packet->len) = newlen;
+
+    return 0;
+}
+
 int sss_packet_recv(struct sss_packet *packet, int fd)
 {
     size_t rb;
diff --git a/server/responder/common/responder_packet.h b/server/responder/common/responder_packet.h
index 5dc8b01..e98707a 100644
--- a/server/responder/common/responder_packet.h
+++ b/server/responder/common/responder_packet.h
@@ -32,6 +32,8 @@ int sss_packet_new(TALLOC_CTX *mem_ctx, size_t size,
                    enum sss_cli_command cmd,
                    struct sss_packet **rpacket);
 int sss_packet_grow(struct sss_packet *packet, size_t size);
+int sss_packet_shrink(struct sss_packet *packet, size_t size);
+int sss_packet_set_size(struct sss_packet *packet, size_t size);
 int sss_packet_recv(struct sss_packet *packet, int fd);
 int sss_packet_send(struct sss_packet *packet, int fd);
 enum sss_cli_command sss_packet_get_cmd(struct sss_packet *packet);
diff --git a/server/responder/nss/nsssrv_cmd.c b/server/responder/nss/nsssrv_cmd.c
index 520bf6d..30fbc15 100644
--- a/server/responder/nss/nsssrv_cmd.c
+++ b/server/responder/nss/nsssrv_cmd.c
@@ -1349,73 +1349,87 @@ done:
  * GROUP db related functions
  ***************************************************************************/
 
+#define GID_ROFFSET 0
+#define MNUM_ROFFSET sizeof(uint32_t)
+#define STRS_ROFFSET 2*sizeof(uint32_t)
+
 static int fill_grent(struct sss_packet *packet,
                       struct sss_domain_info *dom,
                       struct nss_ctx *nctx,
                       bool filter_groups,
                       struct ldb_message **msgs,
-                      int count)
+                      int max, int *count)
 {
-    struct ldb_message_element *el;
     struct ldb_message *msg;
     uint8_t *body;
-    const char *name;
+    size_t blen;
     uint32_t gid, uid;
-    size_t rsize, rp, blen, mnump;
-    int i, j, n, t, ret, num, memnum;
-    bool get_members;
-    bool skip_members;
-    size_t dom_len = 0;
-    size_t name_len;
-    int delim = 1;
+    const char *name;
+    size_t nsize;
+    size_t delim;
+    size_t dom_len;
+    int i, ret, num, memnum;
+    size_t rzero, rsize;
     bool add_domain = dom->fqnames;
     const char *domain = dom->name;
     const char *namefmt = nctx->rctx->names->fq_fmt;
-    bool packet_initialized = false;
-    int ncret;
-    bool legacy = false;
 
-    if (add_domain) dom_len = strlen(domain);
-
-    rp = 2*sizeof(uint32_t);
+    if (add_domain) {
+        delim = 1;
+        dom_len = strlen(domain);
+    } else {
+        delim = 0;
+        dom_len = 0;
+    }
 
     num = 0;
-    mnump = 0;
     memnum = 0;
-    get_members = false;
-    skip_members = false;
-    for (i = 0; i < count; i++) {
+
+    /* first 2 fields (len and reserved), filled up later */
+    ret = sss_packet_grow(packet, 2*sizeof(uint32_t));
+    if (ret != EOK) {
+        goto done;
+    }
+    sss_packet_get_body(packet, &body, &blen);
+    rzero = 2*sizeof(uint32_t);
+    rsize = 0;
+
+    for (i = 0; i < *count; i++) {
         msg = msgs[i];
 
         /* new group */
         if (ldb_msg_check_string_attribute(msg, "objectClass",
                                                 SYSDB_GROUP_CLASS)) {
-            if (get_members) {
-                /* this marks the end of a previous group */
-                sss_packet_get_body(packet, &body, &blen);
-                ((uint32_t *)(&body[mnump]))[0] = memnum; /* num members */
-                get_members = false;
+            if (memnum) {
+                /* set num of members */
+                ((uint32_t *)(&body[rzero+MNUM_ROFFSET]))[0] = memnum;
+                memnum = 0;
+            }
+
+            /* if we reached the max allowed entries, simply return */
+            if (num >= max) {
+                goto done;
             }
 
+            /* new result starts at end of previous result */
+            rzero += rsize;
+            rsize = 0;
+
             /* find group name/gid */
             name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
             gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
             if (!name || !gid) {
-                DEBUG(1, ("Incomplete group object for %s[%llu]! Aborting\n",
+                DEBUG(1, ("Incomplete group object for %s[%llu]! Skipping\n",
                           name?name:"<NULL>", (unsigned long long int)gid));
-                num = 0;
-                goto done;
+                continue;
             }
 
-            skip_members = false;
-
             if (filter_groups) {
-                ncret = nss_ncache_check_group(nctx->ncache,
+                ret = nss_ncache_check_group(nctx->ncache,
                                              nctx->neg_timeout, domain, name);
-                if (ncret == EEXIST) {
+                if (ret == EEXIST) {
                     DEBUG(4, ("Group [%s@%s] filtered out! (negative cache)\n",
                               name, domain));
-                    skip_members = true;
                     continue;
                 }
             }
@@ -1423,23 +1437,16 @@ static int fill_grent(struct sss_packet *packet,
             /* check that the gid is valid for this domain */
             if ((dom->id_min && (gid < dom->id_min)) ||
                 (dom->id_max && (gid > dom->id_max))) {
-                    DEBUG(4, ("User [%s@%s] filtered out! (id out of range)\n",
+                    DEBUG(4, ("Group [%s@%s] filtered out! (id out of range)\n",
                               name, domain));
-                skip_members = true;
                 continue;
             }
 
-            if (!packet_initialized) {
-                /* first 2 fields (len and reserved), filled up later */
-                ret = sss_packet_grow(packet, 2*sizeof(uint32_t));
-                if (ret != EOK) return ret;
-                packet_initialized = true;
-            }
+            nsize = strlen(name) + 1; /* includes terminating \0 */
+            if (add_domain) nsize += delim + dom_len;
 
             /* fill in gid and name and set pointer for number of members */
-            name_len = strlen(name) + 1;
-            if (add_domain) name_len += delim + dom_len;
-            rsize = 2 * sizeof(uint32_t) + name_len + 2;
+            rsize = STRS_ROFFSET + nsize + 2; /* name\0x\0 */
 
             ret = sss_packet_grow(packet, rsize);
             if (ret != EOK) {
@@ -1448,153 +1455,82 @@ static int fill_grent(struct sss_packet *packet,
             }
             sss_packet_get_body(packet, &body, &blen);
 
-            /*  0-3: 64bit number gid */
-            rp = blen - rsize;
-            ((uint32_t *)(&body[rp]))[0] = gid;
-            rp += sizeof(uint32_t);
+            /*  0-3: 32bit number gid */
+            ((uint32_t *)(&body[rzero+GID_ROFFSET]))[0] = gid;
 
             /*  4-7: 32bit unsigned number of members */
-            ((uint32_t *)(&body[rp]))[0] = 0; /* init members num to 0 */
-            mnump = rp; /* keep around members num pointer to set later */
-            rp += sizeof(uint32_t);
+            ((uint32_t *)(&body[rzero+MNUM_ROFFSET]))[0] = 0;
 
             /*  8-X: sequence of strings (name, passwd, mem..) */
             if (add_domain) {
-                ret = snprintf((char *)&body[rp], name_len, namefmt, name, domain);
-                if (ret >= name_len) {
+                ret = snprintf((char *)&body[rzero+STRS_ROFFSET],
+                                nsize, namefmt, name, domain);
+                if (ret >= nsize) {
                     /* need more space, got creative with the print format ? */
-                    t = ret - name_len + 1;
+                    int t = ret - nsize + 1;
                     ret = sss_packet_grow(packet, t);
                     if (ret != EOK) {
                         num = 0;
                         goto done;
                     }
-                    delim += t;
-                    name_len += t;
                     sss_packet_get_body(packet, &body, &blen);
+                    rsize += t;
+                    delim += t;
+                    nsize += t;
 
                     /* retry */
-                    ret = snprintf((char *)&body[rp], name_len, namefmt, name, domain);
-                }
-
-                if (ret != name_len-1) {
-                    DEBUG(1, ("Failed to generate a fully qualified name for user "
-                              "[%s] in [%s]! Skipping user.\n", name, domain));
-                    continue;
+                    ret = snprintf((char *)&body[rzero+STRS_ROFFSET],
+                                    nsize, namefmt, name, domain);
                 }
-            } else {
-                memcpy(&body[rp], name, name_len);
-            }
-            rp += name_len;
-
-            body[rp] = 'x'; /* group passwd field */
-            body[rp+1] = '\0';
-
-            memnum = 0;
-            num++;
-
-            /* legacy style group, members are in SYSDB_LEGACY_MEMBER */
-            el = ldb_msg_find_element(msg, SYSDB_LEGACY_MEMBER);
-            if (el) {
-                /* legacy */
-                legacy = true;
-                memnum = el->num_values;
-                n = 0;
-                for (j = 0; j < memnum; j++) {
-
-                    name = (char *)el->values[j].data;
-
-                    if (nctx->filter_users_in_groups) {
-                        ncret = nss_ncache_check_user(nctx->ncache,
-                                                      nctx->neg_timeout,
-                                                      domain, name);
-                        if (ncret == EEXIST) {
-                            DEBUG(4,("User [%s@%s] filtered out! (negative cache)\n",
-                                      name, domain));
-                            continue;
-                        }
-                    }
 
-                    name_len = el->values[j].length + 1;
-                    if (add_domain) name_len += delim + dom_len;
-
-                    rsize = name_len;
-                    ret = sss_packet_grow(packet, rsize);
+                if (ret != nsize-1) {
+                    DEBUG(1, ("Failed to generate a fully qualified name for"
+                              " group [%s] in [%s]! Skipping\n", name, domain));
+                    /* reclaim space */
+                    ret = sss_packet_shrink(packet, rsize);
                     if (ret != EOK) {
                         num = 0;
                         goto done;
                     }
-
-                    sss_packet_get_body(packet, &body, &blen);
-                    rp = blen - rsize;
-
-                    if (add_domain) {
-                        ret = snprintf((char *)&body[rp], name_len, namefmt, name, domain);
-                        if (ret >= name_len) {
-                            /* need more space, got creative with the print format ? */
-                            t = ret - name_len + 1;
-                            ret = sss_packet_grow(packet, t);
-                            if (ret != EOK) {
-                                num = 0;
-                                goto done;
-                            }
-                            delim += t;
-                            name_len += t;
-                            sss_packet_get_body(packet, &body, &blen);
-
-                            /* retry */
-                            ret = snprintf((char *)&body[rp], name_len, namefmt, name, domain);
-                        }
-
-                        if (ret != name_len-1) {
-                            DEBUG(1, ("Failed to generate a fully qualified name for user "
-                                      "[%s] in [%s]! Skipping user.\n", name, domain));
-                            continue;
-                        }
-                    } else {
-                        memcpy(&body[rp], name, name_len);
-                    }
-                    rp += name_len;
-                    body[blen-1] = '\0';
-
-                    n++;
+                    rsize = 0;
+                    continue;
                 }
-
-                sss_packet_get_body(packet, &body, &blen);
-                ((uint32_t *)(&body[mnump]))[0] = n; /* num members */
-
-            }  else {
-                get_members = true;
+            } else {
+                memcpy(&body[rzero+STRS_ROFFSET], name, nsize);
             }
 
+            body[rzero + rsize -2] = 'x'; /* group passwd field */
+            body[rzero + rsize -1] = '\0';
+
+            num++;
             continue;
         }
 
-        if (skip_members) continue;
-
-        if (!get_members) {
-            DEBUG(1, ("Wrong object found on stack! Aborting\n"));
-            num = 0;
-            goto done;
+        if (rsize == 0) {
+            /* some error occurred and there is no result structure ready to
+             * store members */
+            continue;
         }
 
         /* member */
         if (ldb_msg_check_string_attribute(msg, "objectClass",
                                                 SYSDB_USER_CLASS)) {
 
+            nsize = 0;
+
             name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
             uid = ldb_msg_find_attr_as_uint64(msg, SYSDB_UIDNUM, 0);
             if (!name || !uid) {
-                DEBUG(1, ("Incomplete user object! Aborting\n"));
-                num = 0;
-                goto done;
+                DEBUG(1, ("Incomplete user object! Skipping\n"));
+                continue;
             }
 
             if (nctx->filter_users_in_groups) {
-                ncret = nss_ncache_check_user(nctx->ncache,
+                ret = nss_ncache_check_user(nctx->ncache,
                                             nctx->neg_timeout, domain, name);
-                if (ncret == EEXIST) {
-                    DEBUG(4, ("User [%s@%s] filtered out! (negative cache)\n",
+                if (ret == EEXIST) {
+                    DEBUG(4, ("Group [%s] member [%s@%s] filtered out! (negative"
+                              " cache)\n", (char *)&body[rzero+STRS_ROFFSET],
                               name, domain));
                     continue;
                 }
@@ -1603,72 +1539,84 @@ static int fill_grent(struct sss_packet *packet,
             /* check that the uid is valid for this domain */
             if ((dom->id_min && (uid < dom->id_min)) ||
                 (dom->id_max && (uid > dom->id_max))) {
-                    DEBUG(4, ("User [%s@%s] filtered out! (id out of range)\n",
+                    DEBUG(4, ("Group [%s] member [%s@%s] filtered out! (id out"
+                              " of range)\n", (char *)&body[rzero+STRS_ROFFSET],
                               name, domain));
                 continue;
             }
 
-            name_len = strlen(name) + 1;
-            if (add_domain) name_len += delim + dom_len;
+            nsize = strlen(name) + 1;
+            if (add_domain) nsize += delim + dom_len;
 
-            rsize = name_len;
-            ret = sss_packet_grow(packet, rsize);
+            ret = sss_packet_grow(packet, nsize);
             if (ret != EOK) {
                 num = 0;
                 goto done;
             }
             sss_packet_get_body(packet, &body, &blen);
-            rp = blen - rsize;
 
             if (add_domain) {
-                ret = snprintf((char *)&body[rp], name_len, namefmt, name, domain);
-                if (ret >= name_len) {
+                ret = snprintf((char *)&body[rzero + rsize],
+                                nsize, namefmt, name, domain);
+                if (ret >= nsize) {
                     /* need more space, got creative with the print format ? */
-                    t = ret - name_len + 1;
+                    int t = ret - nsize + 1;
                     ret = sss_packet_grow(packet, t);
                     if (ret != EOK) {
                         num = 0;
                         goto done;
                     }
-                    delim += t;
-                    name_len += t;
                     sss_packet_get_body(packet, &body, &blen);
+                    delim += t;
+                    nsize += t;
 
                     /* retry */
-                    ret = snprintf((char *)&body[rp], name_len, namefmt, name, domain);
+                    ret = snprintf((char *)&body[rzero + rsize],
+                                    nsize, namefmt, name, domain);
                 }
 
-                if (ret != name_len-1) {
-                    DEBUG(1, ("Failed to generate a fully qualified name for user "
-                              "[%s] in [%s]! Skipping user.\n", name, domain));
+                if (ret != nsize-1) {
+                    DEBUG(1, ("Failed to generate a fully qualified name for"
+                              " member [%s@%s] of group [%s]! Skipping\n", name,
+                              domain, (char *)&body[rzero+STRS_ROFFSET]));
+                    /* reclaim space */
+                    ret = sss_packet_shrink(packet, nsize);
+                    if (ret != EOK) {
+                        num = 0;
+                        goto done;
+                    }
                     continue;
                 }
             } else {
-                memcpy(&body[rp], name, name_len);
+                memcpy(&body[rzero + rsize], name, nsize);
             }
-            rp += name_len;
-            memnum++;
 
+            rsize += nsize;
+            memnum++;
             continue;
         }
 
-        DEBUG(1, ("Wrong object found on stack! Aborting\n"));
-        num = 0;
-        goto done;
+        DEBUG(1, ("Wrong object (%s) found on stack!\n",
+                  ldb_dn_get_linearized(msg->dn)));
+        continue;
     }
 
-    if (mnump && !legacy) {
-        /* fill in the last group member count */
-        sss_packet_get_body(packet, &body, &blen);
-        ((uint32_t *)(&body[mnump]))[0] = memnum; /* num members */
+    if (memnum) {
+        /* set num of members for the last result */
+        ((uint32_t *)(&body[rzero+MNUM_ROFFSET]))[0] = memnum;
     }
 
 done:
-    /* if there are no results just return ENOENT,
-     * let the caller decide if this is the last packet or not */
-    if (!packet_initialized) return ENOENT;
+    *count = i;
+
+    if (num == 0) {
+        /* if num is 0 most probably something went wrong,
+         * reset packet and return ENOENT */
+        ret = sss_packet_set_size(packet, 0);
+        if (ret != EOK) return ret;
+        return ENOENT;
+    }
 
-    sss_packet_get_body(packet, &body, &blen);
     ((uint32_t *)body)[0] = num; /* num results */
     ((uint32_t *)body)[1] = 0; /* reserved */
 
@@ -1679,7 +1627,7 @@ static void nss_cmd_getgrnam_dp_callback(uint16_t err_maj, uint32_t err_min,
                                       const char *err_msg, void *ptr);
 
 static void nss_cmd_getgrnam_callback(void *ptr, int status,
-                                   struct ldb_result *res)
+                                      struct ldb_result *res)
 {
     struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx);
     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
@@ -1693,7 +1641,7 @@ static void nss_cmd_getgrnam_callback(void *ptr, int status,
     bool call_provider = false;
     bool neghit = false;
     int ncret;
-    int ret;
+    int i, ret;
 
     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
 
@@ -1837,10 +1785,11 @@ static void nss_cmd_getgrnam_callback(void *ptr, int status,
         if (ret != EOK) {
             NSS_CMD_FATAL_ERROR(cctx);
         }
+        i = res->count;
         ret = fill_grent(cctx->creq->out,
                          dctx->domain,
                          nctx, false,
-                         res->msgs, res->count);
+                         res->msgs, 1, &i);
         if (ret == ENOENT) {
             ret = fill_empty(cctx->creq->out);
         }
@@ -2049,7 +1998,7 @@ static void nss_cmd_getgrgid_callback(void *ptr, int status,
     size_t blen;
     bool call_provider = false;
     bool neghit = false;
-    int ret;
+    int i, ret;
     int ncret;
 
     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
@@ -2181,11 +2130,11 @@ static void nss_cmd_getgrgid_callback(void *ptr, int status,
         if (ret != EOK) {
             NSS_CMD_FATAL_ERROR(cctx);
         }
-
+        i = res->count;
         ret = fill_grent(cctx->creq->out,
                          dctx->domain,
                          nctx, true,
-                         res->msgs, res->count);
+                         res->msgs, 1, &i);
         if (ret == ENOENT) {
             ret = fill_empty(cctx->creq->out);
         }
@@ -2626,13 +2575,12 @@ retry:
 
     if (!n) goto none;
 
-    if (n > num) n = num;
-
     msgs = &(gdom->res->msgs[gdom->cur]);
-    gdom->cur += n;
 
-    ret = fill_grent(cctx->creq->out, gdom->domain, nctx, true, msgs, n);
+    ret = fill_grent(cctx->creq->out, gdom->domain, nctx, true, msgs, num, &n);
     if (ret == ENOENT) goto retry;
+
+    gdom->cur += n;
     return ret;
 
 none:
-- 
1.6.2.5

