about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/include/nsss/grp-unix.h1
-rw-r--r--src/include/nsss/nsss-switch.h4
-rw-r--r--src/include/nsss/nsss-unix.h2
-rw-r--r--src/include/nsss/nsssd.h1
-rw-r--r--src/libnsss/deps-lib/nsss7
-rw-r--r--src/libnsss/nsss-internal.h2
-rw-r--r--src/libnsss/nsss_all_getgrouplist.c35
-rw-r--r--src/libnsss/nsss_grouplist_adjust.c24
-rw-r--r--src/libnsss/nsss_switch_getgrouplist.c30
-rw-r--r--src/libnsss/nsss_switch_grp_getlist.c66
-rw-r--r--src/libnsss/nsss_unix_getgrouplist.c34
-rw-r--r--src/libnsss/nsss_unix_getgrouplist_preadjust.c22
-rw-r--r--src/libnsss/nsss_unix_grp_getlist.c19
-rw-r--r--src/nsssd/nsssd-nslcd.c11
-rw-r--r--src/nsssd/nsssd-unix.c6
-rw-r--r--src/nsssd/nsssd_main.c50
16 files changed, 270 insertions, 44 deletions
diff --git a/src/include/nsss/grp-unix.h b/src/include/nsss/grp-unix.h
index 3b198c0..862fbf2 100644
--- a/src/include/nsss/grp-unix.h
+++ b/src/include/nsss/grp-unix.h
@@ -17,5 +17,6 @@ extern struct group *nsss_unix_getgrnam (char const *) ;
 extern int nsss_unix_getgrgid_r (gid_t, struct group *, char *, size_t, struct group **) ;
 extern int nsss_unix_getgrnam_r (char const *, struct group *, char *, size_t, struct group **) ;
 extern int nsss_unix_getgrouplist (char const *, gid_t, gid_t *, int *) ;
+extern int nsss_unix_getgrouplist_preadjust (char const *, gid_t *, size_t, size_t *) ;
 
 #endif
diff --git a/src/include/nsss/nsss-switch.h b/src/include/nsss/nsss-switch.h
index 9e1a850..1f40f7c 100644
--- a/src/include/nsss/nsss-switch.h
+++ b/src/include/nsss/nsss-switch.h
@@ -79,8 +79,8 @@ extern int nsss_switch_grp_getbyname (nsss_switch_t *, struct group *, stralloc
 #define nsss_switch_grp_getbyname_g(a, b, sa, ga, name, deadline) nsss_switch_grp_getbyname(a, b, sa, ga, name, (deadline), &STAMP)
 extern int nsss_switch_grp_getbygid (nsss_switch_t *, struct group *, stralloc *, genalloc *, gid_t, tain_t const *, tain_t *) ;
 #define nsss_switch_grp_getbygid_g(a, b, sa, ga, g, deadline) nsss_switch_grp_getbygid(a, b, sa, ga, g, (deadline), &STAMP)
-extern int nsss_switch_grp_getlist (nsss_switch_t *, char const *, gid_t, genalloc *, tain_t const *, tain_t *) ;
-#define nsss_switch_grp_getlist_g(a, user, g, ga, deadline) nsss_switch_grp_getlist(a, user, g, ga, (deadline), &STAMP)
+extern int nsss_switch_grp_getlist (nsss_switch_t *, char const *, gid_t *, size_t, size_t *, stralloc *, tain_t const *, tain_t *) ;
+#define nsss_switch_grp_getlist_g(a, user, gids, n, r, sa, deadline) nsss_switch_grp_getlist(a, user, gids, n, r, sa, (deadline), &STAMP)
 
 
  /* Shadow */
diff --git a/src/include/nsss/nsss-unix.h b/src/include/nsss/nsss-unix.h
index dfb0bde..e48eaec 100644
--- a/src/include/nsss/nsss-unix.h
+++ b/src/include/nsss/nsss-unix.h
@@ -48,7 +48,7 @@ extern int nsss_unix_pwd_getbyuid (nsss_unix_t *, struct passwd *, stralloc *, u
 extern int nsss_unix_grp_get (nsss_unix_t *, struct group *, stralloc *, genalloc *) ;
 extern int nsss_unix_grp_getbyname (nsss_unix_t *, struct group *, stralloc *, genalloc *, char const *) ;
 extern int nsss_unix_grp_getbygid (nsss_unix_t *, struct group *, stralloc *, genalloc *, gid_t) ;
-extern int nsss_unix_grp_getlist (nsss_unix_t *, gid_t *, size_t, size_t *, stralloc *, genalloc *, char const *) ;
+extern int nsss_unix_grp_getlist (nsss_unix_t *, char const *, gid_t *, size_t, size_t *, stralloc *, genalloc *) ;
 
 
  /* Shadow */
diff --git a/src/include/nsss/nsssd.h b/src/include/nsss/nsssd.h
index 7649873..b21e6d2 100644
--- a/src/include/nsss/nsssd.h
+++ b/src/include/nsss/nsssd.h
@@ -73,6 +73,7 @@ extern int nsssd_grp_rewind (void *) ;
 extern int nsssd_grp_get (void *, struct group *) ;
 extern int nsssd_grp_getbygid (void *, struct group *, gid_t) ;
 extern int nsssd_grp_getbyname (void *, struct group *, char const *) ;
+extern int nsssd_grp_getlist (void *, char const *, gid_t *, size_t, size_t *) ;
 extern void nsssd_grp_end (void *) ;
 
 extern int nsssd_shadow_start (void *) ;
diff --git a/src/libnsss/deps-lib/nsss b/src/libnsss/deps-lib/nsss
index 1886bdf..e00084e 100644
--- a/src/libnsss/deps-lib/nsss
+++ b/src/libnsss/deps-lib/nsss
@@ -8,6 +8,7 @@ nsss_all_getgrgid.o
 nsss_all_getgrgid_r.o
 nsss_all_getgrnam.o
 nsss_all_getgrnam_r.o
+nsss_all_getgrouplist.o
 nsss_all_getpwent.o
 nsss_all_getpwent_r.o
 nsss_all_getpwnam.o
@@ -23,6 +24,7 @@ nsss_all_setpwent.o
 nsss_all_setspent.o
 nsss_grp_copy.o
 nsss_grp_here.o
+nsss_grouplist_adjust.o
 nsss_pwd_copy.o
 nsss_pwd_here.o
 nsss_shadow_copy.o
@@ -37,6 +39,7 @@ nsss_switch_getgrgid.o
 nsss_switch_getgrgid_r.o
 nsss_switch_getgrnam.o
 nsss_switch_getgrnam_r.o
+nsss_switch_getgrouplist.o
 nsss_switch_getpwent.o
 nsss_switch_getpwent_r.o
 nsss_switch_getpwnam.o
@@ -51,6 +54,7 @@ nsss_switch_grp_end.o
 nsss_switch_grp_get.o
 nsss_switch_grp_getbygid.o
 nsss_switch_grp_getbyname.o
+nsss_switch_grp_getlist.o
 nsss_switch_grp_read.o
 nsss_switch_grp_rewind.o
 nsss_switch_here.o
@@ -81,6 +85,8 @@ nsss_unix_getgrgid.o
 nsss_unix_getgrgid_r.o
 nsss_unix_getgrnam.o
 nsss_unix_getgrnam_r.o
+nsss_unix_getgrouplist.o
+nsss_unix_getgrouplist_preadjust.o
 nsss_unix_getpwent.o
 nsss_unix_getpwent_r.o
 nsss_unix_getpwnam.o
@@ -91,7 +97,6 @@ nsss_unix_getspent.o
 nsss_unix_getspent_r.o
 nsss_unix_getspnam.o
 nsss_unix_getspnam_r.o
-nsss_unix_getgrouplist.o
 nsss_unix_grp_get.o
 nsss_unix_grp_getbygid.o
 nsss_unix_grp_getbyname.o
diff --git a/src/libnsss/nsss-internal.h b/src/libnsss/nsss-internal.h
index f9dbeb3..a67b5f7 100644
--- a/src/libnsss/nsss-internal.h
+++ b/src/libnsss/nsss-internal.h
@@ -24,7 +24,7 @@ extern struct group nsss_grp_here ;
 extern stralloc nsss_grp_sa_here ;
 extern genalloc nsss_grp_ga_here ;
 extern int nsss_grp_copy (struct group *, char *, size_t, struct group const *, char const *, size_t, char *const *, size_t) ;
-
+extern int nsss_grouplist_adjust (size_t, size_t, gid_t, gid_t *, int *, int) ;
 
  /* Shadow */
 
diff --git a/src/libnsss/nsss_all_getgrouplist.c b/src/libnsss/nsss_all_getgrouplist.c
new file mode 100644
index 0000000..0560ba7
--- /dev/null
+++ b/src/libnsss/nsss_all_getgrouplist.c
@@ -0,0 +1,35 @@
+/* ISC license. */
+
+#include <stddef.h>
+#include <errno.h>
+
+#include <skalibs/stralloc.h>
+
+#include <nsss/config.h>
+#include <nsss/grp-unix.h>
+#include <nsss/nsss-switch.h>
+#include <nsss/grp-all.h>
+#include "nsss-internal.h"
+
+int nsss_all_getgrouplist (char const *user, gid_t gid, gid_t *gids, int *ngids)
+{
+  stralloc sa = STRALLOC_ZERO ;
+  int e = errno ;
+  size_t r = 0 ;
+  size_t n ;
+  nsss_switch_t a = NSSS_SWITCH_ZERO ;
+  if (*ngids < 0) return (errno = EINVAL, -1) ;
+  n = *ngids ;
+  if (!nsss_switch_start(&a, NSSS_SWITCH_GRP, NSSS_NSSSD_PATH, 0, 0)) goto fallback ;
+  if (!nsss_switch_grp_getlist(&a, user, gids, n, &r, &sa, 0, 0))
+  {
+    nsss_switch_end(&a, NSSS_SWITCH_GRP) ;
+    return -1 ;
+  }
+  nsss_switch_end(&a, NSSS_SWITCH_GRP) ;
+  return nsss_grouplist_adjust(n, r, gid, gids, ngids, e) ;
+
+ fallback:
+  errno = e ;
+  return nsss_unix_getgrouplist(user, gid, gids, ngids) ;
+}
diff --git a/src/libnsss/nsss_grouplist_adjust.c b/src/libnsss/nsss_grouplist_adjust.c
new file mode 100644
index 0000000..fda4882
--- /dev/null
+++ b/src/libnsss/nsss_grouplist_adjust.c
@@ -0,0 +1,24 @@
+/* ISC license. */
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <nsss/grp-def.h>
+#include "nsss-internal.h"
+
+int nsss_grouplist_adjust (size_t n, size_t r, gid_t gid, gid_t *gids, int *ngids, int e)
+{
+  if (r > INT_MAX) return (errno = EMSGSIZE, -1) ;
+  if (r > n) return (*ngids = (int)r, errno = ENOBUFS, -1) ;
+  for (size_t i = 0 ; i < r ; i++) if (gid == gids[i]) goto ok ;
+  r++ ;
+  if (r > INT_MAX) return (errno = EMSGSIZE, -1) ;
+  if (r > n) return (*ngids = (int)r, errno = ENOBUFS, -1) ;
+  memmove(gids + 1, gids, (r-1) * sizeof(gid_t)) ;
+  gids[0] = gid ;
+ ok:
+  *ngids = (int)r ;
+  errno = e ;
+  return (int)n ;
+}
diff --git a/src/libnsss/nsss_switch_getgrouplist.c b/src/libnsss/nsss_switch_getgrouplist.c
new file mode 100644
index 0000000..73d3fe0
--- /dev/null
+++ b/src/libnsss/nsss_switch_getgrouplist.c
@@ -0,0 +1,30 @@
+/* ISC license. */
+
+#include <stddef.h>
+#include <errno.h>
+
+#include <skalibs/stralloc.h>
+
+#include <nsss/config.h>
+#include <nsss/nsss-switch.h>
+#include <nsss/grp-switch.h>
+#include "nsss-internal.h"
+
+int nsss_switch_getgrouplist (char const *user, gid_t gid, gid_t *gids, int *ngids)
+{
+  stralloc sa = STRALLOC_ZERO ;
+  int e = errno ;
+  size_t r = 0 ;
+  size_t n ;
+  nsss_switch_t a = NSSS_SWITCH_ZERO ;
+  if (*ngids < 0) return (errno = EINVAL, -1) ;
+  n = *ngids ;
+  if (!nsss_switch_start(&a, NSSS_SWITCH_GRP, NSSS_NSSSD_PATH, 0, 0)) return -1 ;
+  if (!nsss_switch_grp_getlist(&a, user, gids, n, &r, &sa, 0, 0))
+  {
+    nsss_switch_end(&a, NSSS_SWITCH_GRP) ;
+    return -1 ;
+  }
+  nsss_switch_end(&a, NSSS_SWITCH_GRP) ;
+  return nsss_grouplist_adjust(n, r, gid, gids, ngids, e) ;
+}
diff --git a/src/libnsss/nsss_switch_grp_getlist.c b/src/libnsss/nsss_switch_grp_getlist.c
new file mode 100644
index 0000000..70e4486
--- /dev/null
+++ b/src/libnsss/nsss_switch_grp_getlist.c
@@ -0,0 +1,66 @@
+/* ISC license. */
+
+#include <string.h>
+#include <sys/uio.h>
+#include <errno.h>
+
+#include <skalibs/posixishard.h>
+#include <skalibs/uint32.h>
+#include <skalibs/uint64.h>
+#include <skalibs/types.h>
+#include <skalibs/buffer.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/unix-timed.h>
+
+#include <nsss/nsss-switch.h>
+
+ /*
+    Expects:
+      8 bytes: n
+      8 bytes: r
+      min(n, r) * sizeof(gid_t) bytes: the list of gids
+  */
+
+static int nsss_switch_grouplist_read (buffer *b, size_t n, size_t *r, gid_t *gids, stralloc *sa, tain_t const *deadline, tain_t *stamp)
+{
+  size_t rr ;
+  uint64_t res ;
+  char buf[8] ;
+  if (!buffer_timed_get(b, buf, 8, deadline, stamp)) return 0 ;
+  uint64_unpack_big(buf, &res) ;
+  if (res != n) return (errno = EPROTO, 0) ;
+  if (!buffer_timed_get(b, buf, 8, deadline, stamp)) return 0 ;
+  uint64_unpack_big(buf, &res) ;
+  rr = res ;
+  if (res > n) res = n ;
+  if (res)
+  {
+    int sawasnull = !sa->s ;
+    size_t sabase = sa->len ;
+    if (!stralloc_readyplus(sa, res * sizeof(gid_t))) return 0 ;
+    if (!buffer_timed_get(b, sa->s + sabase, res * sizeof(gid_t), deadline, stamp))
+    {
+      if (sawasnull) stralloc_free(sa) ; else sa->len = sabase ;
+      return 0 ;
+    }
+    for (size_t i = 0 ; i < res ; i++)
+      gid_unpack_big(sa->s + sabase + i * sizeof(gid_t), gids + i) ;
+    if (sawasnull) stralloc_free(sa) ; else sa->len = sabase ;  
+  }
+  *r = rr ;
+  return 1 ;
+}
+
+int nsss_switch_grp_getlist (nsss_switch_t *a, char const *user, gid_t *gids, size_t n, size_t *r, stralloc *sa, tain_t const *deadline, tain_t *stamp)
+{
+  char buf[13] = { NSSS_SWITCH_GRP_GETLIST } ;
+  size_t len = strlen(user) ;
+  struct iovec v[2] = { { .iov_base = buf, .iov_len = 13 }, { .iov_base = (char *)user, .iov_len = len + 1 } } ;
+  if (len > NSSS_SWITCH_NAME_MAXLEN - 1) return (errno = EINVAL, 0) ;
+  uint64_pack_big(buf + 1, n) ;
+  uint32_pack_big(buf + 9, len + 1) ;
+  if (!ipc_timed_sendv(buffer_fd(&a->b), v, 2, deadline, stamp)) return 0 ;
+  if (!buffer_timed_get(&a->b, &buf[0], 1, deadline, stamp)) return 0 ;
+  if (buf[0]) return (errno = buf[0], 0) ;
+  return nsss_switch_grouplist_read(&a->b, n, r, gids, sa, deadline, stamp) ;
+}
diff --git a/src/libnsss/nsss_unix_getgrouplist.c b/src/libnsss/nsss_unix_getgrouplist.c
index e9a11ab..83a8aeb 100644
--- a/src/libnsss/nsss_unix_getgrouplist.c
+++ b/src/libnsss/nsss_unix_getgrouplist.c
@@ -1,43 +1,19 @@
 /* ISC license. */
 
-#include <string.h>
-#include <unistd.h>
+#include <stddef.h>
 #include <errno.h>
-#include <limits.h>
 
-#include <skalibs/stralloc.h>
-#include <skalibs/genalloc.h>
-#include <skalibs/skamisc.h>
-
-#include <nsss/grp-def.h>
-#include <nsss/grp-unix.h>
 #include <nsss/nsss-unix.h>
-#include "nsss-unix-internal.h"
+#include <nsss/grp-unix.h>
+#include "nsss-internal.h"
 
 int nsss_unix_getgrouplist (char const *user, gid_t gid, gid_t *gids, int *ngids)
 {
-  nsss_unix_t a = NSSS_UNIX_ZERO ;
-  stralloc sa = STRALLOC_ZERO ;
-  genalloc ga = GENALLOC_ZERO ;  /* char * */
   int e = errno ;
   size_t r = 0 ;
   size_t n ;
   if (*ngids < 0) return (errno = EINVAL, -1) ;
   n = *ngids ;
-  if (!nsss_unix_grp_start(&a)) return -1 ;
-  if (!nsss_unix_grp_getlist(&a, gids, n, &r, &sa, &ga, user)) return -1 ;
-  nsss_unix_grp_end(&a) ;
-  if (r > INT_MAX) return (errno = EMSGSIZE, -1) ;
-  if (r > n) return (*ngids = (int)r, errno = ENOBUFS, -1) ;
-  for (size_t i = 0 ; i < r ; i++)
-    if (gid == gids[i]) goto ok ;
-  r++ ;
-  if (r > INT_MAX) return (errno = EMSGSIZE, -1) ;
-  if (r > n) return (*ngids = (int)r, errno = ENOBUFS, -1) ;
-  memmove(gids + 1, gids, (r-1) * sizeof(gid_t)) ;
-  gids[0] = gid ;
- ok:
-  *ngids = (int)r ;
-  errno = e ;
-  return (int)n ;
+  if (!nsss_unix_getgrouplist_preadjust(user, gids, n, &r)) return -1 ;
+  return nsss_grouplist_adjust(n, r, gid, gids, ngids, e) ;
 }
diff --git a/src/libnsss/nsss_unix_getgrouplist_preadjust.c b/src/libnsss/nsss_unix_getgrouplist_preadjust.c
new file mode 100644
index 0000000..a0dc236
--- /dev/null
+++ b/src/libnsss/nsss_unix_getgrouplist_preadjust.c
@@ -0,0 +1,22 @@
+/* ISC license. */
+
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+
+#include <nsss/nsss-unix.h>
+#include <nsss/grp-unix.h>
+
+int nsss_unix_getgrouplist_preadjust (char const *user, gid_t *gids, size_t n, size_t *r)
+{
+  stralloc sa = STRALLOC_ZERO ;
+  genalloc ga = GENALLOC_ZERO ;  /* char * */
+  nsss_unix_t a = NSSS_UNIX_ZERO ;
+  if (!nsss_unix_grp_start(&a)) return 0 ;
+  if (!nsss_unix_grp_getlist(&a, user, gids, n, r, &sa, &ga))
+  {
+    nsss_unix_grp_end(&a) ;
+    return 0 ;
+  }
+  nsss_unix_grp_end(&a) ;
+  return 1 ;
+}
diff --git a/src/libnsss/nsss_unix_grp_getlist.c b/src/libnsss/nsss_unix_grp_getlist.c
index 9885a03..65a9117 100644
--- a/src/libnsss/nsss_unix_grp_getlist.c
+++ b/src/libnsss/nsss_unix_grp_getlist.c
@@ -9,9 +9,8 @@
 #include <nsss/grp-unix.h>
 #include <nsss/nsss-unix.h>
 
-int nsss_unix_grp_getlist (nsss_unix_t *a, gid_t *gids, size_t n, size_t *r, stralloc *sa, genalloc *ga, char const *user)
+int nsss_unix_grp_getlist (nsss_unix_t *a, char const *user, gid_t *gids, size_t n, size_t *r, stralloc *sa, genalloc *ga)
 {
-  int e = errno ;
   int sawasnull = !sa->s ;
   int gawasnull = !genalloc_s(char *, ga) ;
   size_t sabase = sa->len ;
@@ -20,35 +19,35 @@ int nsss_unix_grp_getlist (nsss_unix_t *a, gid_t *gids, size_t n, size_t *r, str
 
   for (;;)
   {
-    struct group *gr ;
+    struct group gr ;
     sa->len = sabase ;
     genalloc_setlen(char *, ga, gabase) ;
     errno = 0 ;
-    if (!nsss_unix_grp_get(a, gr, sa, ga))
+    if (!nsss_unix_grp_get(a, &gr, sa, ga))
     {
       if (errno) goto err ;
       else break ;
     }
-    for (char **p = gr->gr_mem ; *p ; p++)
+    for (char **p = gr.gr_mem ; *p ; p++)
       if (!strcmp(user, *p))
       {
-        if (m < n) gids[m] = gr->gr_gid ;
+        if (m < n) gids[m] = gr.gr_gid ;
         m++ ;
         break ;
       }
   }
 
-  if (gawasnull) genalloc_free(char *, &ga) ;
+  if (gawasnull) genalloc_free(char *, ga) ;
   else genalloc_setlen(char *, ga, gabase) ;
-  if (sawasnull) stralloc_free(&sa) ;
+  if (sawasnull) stralloc_free(sa) ;
   else sa->len = sabase ;
   *r = m ;
   return 1 ;
 
  err:
-  if (gawasnull) genalloc_free(char *, &ga) ;
+  if (gawasnull) genalloc_free(char *, ga) ;
   else genalloc_setlen(char *, ga, gabase) ;
-  if (sawasnull) stralloc_free(&sa) ;
+  if (sawasnull) stralloc_free(sa) ;
   else sa->len = sabase ;
   return 0 ;
 }
diff --git a/src/nsssd/nsssd-nslcd.c b/src/nsssd/nsssd-nslcd.c
index 8f43ac9..47fb61d 100644
--- a/src/nsssd/nsssd-nslcd.c
+++ b/src/nsssd/nsssd-nslcd.c
@@ -467,6 +467,17 @@ int nsssd_grp_getbyname (void *handle, struct group *gr, char const *name)
   return 0 ;
 }
 
+int nsssd_grp_getlist (void *handle, char const *user, gid_t *gids, size_t n, size_t *r)
+{
+  /* TODO: find a sane way of implementing getgrouplist() over nslcd */
+  (void)handle ;
+  (void)user ;
+  (void)gids ;
+  (void)n ;
+  (void)r ;
+  return (errno = ENOSYS, 0) ;
+}
+
 void nsssd_grp_end (void *handle)
 {
 }
diff --git a/src/nsssd/nsssd-unix.c b/src/nsssd/nsssd-unix.c
index e158c9f..6cbb3f2 100644
--- a/src/nsssd/nsssd-unix.c
+++ b/src/nsssd/nsssd-unix.c
@@ -115,6 +115,12 @@ int nsssd_grp_getbyname (void *handle, struct group *gr, char const *name)
   return 1 ;
 }
 
+int nsssd_grp_getlist (void *handle, char const *user, gid_t *gids, size_t n, size_t *r)
+{
+  (void)handle ;
+  return nsss_unix_getgrouplist_preadjust(user, gids, n, r) ;
+}
+
 void nsssd_grp_end (void *handle)
 {
   nsss_unix_endgrent() ;
diff --git a/src/nsssd/nsssd_main.c b/src/nsssd/nsssd_main.c
index 133a813..2f4c625 100644
--- a/src/nsssd/nsssd_main.c
+++ b/src/nsssd/nsssd_main.c
@@ -11,6 +11,7 @@
 #include <skalibs/types.h>
 #include <skalibs/buffer.h>
 #include <skalibs/strerr2.h>
+#include <skalibs/genalloc.h>
 #include <skalibs/tai.h>
 #include <skalibs/djbunix.h>
 #include <skalibs/unix-timed.h>
@@ -309,6 +310,54 @@ static inline void do_grgid (void *a)
   print_gr(&gr) ;
 }
 
+static inline void do_grlist (void *a)
+{
+  uint64_t n ;
+  uint32_t len ;
+  char buf[12] ;
+  get0(buf, 12) ;
+  uint64_unpack_big(buf, &n) ;
+  uint32_unpack_big(buf + 8, &len) ;
+  if (!len || len > NSSS_SWITCH_NAME_MAXLEN - 1)
+  {
+    answer(EPROTO) ;
+    return ;
+  }
+  {
+    genalloc ga = GENALLOC_ZERO ;
+    size_t r ;
+    char user[len] ;
+    get0(user, len) ;
+    if (user[len-1])
+    {
+      answer(EPROTO) ;
+      return ;
+    }
+    if (!genalloc_ready(gid_t, &ga, n))
+    {
+      answer(errno) ;
+      return ;
+    }
+    if (!nsssd_grp_getlist(a, user, genalloc_s(gid_t, &ga), n, &r))
+    {
+      genalloc_free(gid_t, &ga) ;
+      answer(errno) ;
+      return ;
+    }
+    put1("", 1) ;
+    uint64_pack_big(buf, n) ; put1(buf, 8) ;
+    uint64_pack_big(buf, r) ; put1(buf, 8) ;
+    if (r > n) r = n ;
+    for (size_t i = 0 ; i < r ; i++)
+    {
+      gid_pack_big(buf, genalloc_s(gid_t, &ga)[i]) ;
+      put1(buf, sizeof(gid_t)) ;
+    }
+    genalloc_free(gid_t, &ga) ;
+  }
+  flush1() ;
+}
+
 static inline void do_spend (void *a)
 {
   nsssd_shadow_end(a) ;
@@ -429,6 +478,7 @@ int nsssd_main (char const *const *argv, char const *const *envp)
       case NSSS_SWITCH_GRP_GET : do_grget(a) ; break ;
       case NSSS_SWITCH_GRP_GETBYNAME : do_grnam(a) ; break ;
       case NSSS_SWITCH_GRP_GETBYGID : do_grgid(a) ; break ;
+      case NSSS_SWITCH_GRP_GETLIST : do_grlist(a) ; break ;
       case NSSS_SWITCH_SHADOW_END : do_spend(a) ; break ;
       case NSSS_SWITCH_SHADOW_REWIND : do_sprewind(a) ; break ;
       case NSSS_SWITCH_SHADOW_GET : do_spget(a) ; break ;