From edc962fc71e45ec2a54fe2e3bf29e8caec1685b1 Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Thu, 9 Sep 2010 16:23:36 +0200
Subject: [PATCH] Netgroups sysdb API

---
 src/db/sysdb.c        |    6 +
 src/db/sysdb.h        |  100 ++++++++++++++-
 src/db/sysdb_ops.c    |  346 ++++++++++++++++++++++++++++++++++++++++++++++++-
 src/db/sysdb_search.c |   87 ++++++++++++
 4 files changed, 537 insertions(+), 2 deletions(-)

diff --git a/src/db/sysdb.c b/src/db/sysdb.c
index 4e1243c08d9e138193bc106f448013c7c97f8a3a..7be23a19281fa0f1c8b4893214fd1f24edaa1f51 100644
--- a/src/db/sysdb.c
+++ b/src/db/sysdb.c
@@ -52,6 +52,12 @@ struct ldb_dn *sysdb_group_dn(struct sysdb_ctx *ctx, void *memctx,
     return ldb_dn_new_fmt(memctx, ctx->ldb, SYSDB_TMPL_GROUP, name, domain);
 }
 
+struct ldb_dn *sysdb_netgroup_dn(struct sysdb_ctx *ctx, void *memctx,
+                                 const char *domain, const char *name)
+{
+    return ldb_dn_new_fmt(memctx, ctx->ldb, SYSDB_TMPL_NETGROUP, name, domain);
+}
+
 errno_t sysdb_group_dn_name(struct sysdb_ctx *ctx, void *memctx,
                             const char *_dn, char **_name)
 {
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index d8a78badb8500d28a1a51f148afd56eb7b2441a2..b4affbc197a66db28e27eb73ffe078d983330ceb 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -35,12 +35,15 @@
 #define SYSDB_USERS_CONTAINER "cn=users"
 #define SYSDB_GROUPS_CONTAINER "cn=groups"
 #define SYSDB_CUSTOM_CONTAINER "cn=custom"
+#define SYSDB_NETGROUP_CONTAINER "cn=Netgroups"
 #define SYSDB_TMPL_USER_BASE SYSDB_USERS_CONTAINER",cn=%s,"SYSDB_BASE
 #define SYSDB_TMPL_GROUP_BASE SYSDB_GROUPS_CONTAINER",cn=%s,"SYSDB_BASE
 #define SYSDB_TMPL_CUSTOM_BASE SYSDB_CUSTOM_CONTAINER",cn=%s,"SYSDB_BASE
+#define SYSDB_TMPL_NETGROUP_BASE SYSDB_NETGROUP_CONTAINER",cn=%s,"SYSDB_BASE
 
 #define SYSDB_USER_CLASS "user"
 #define SYSDB_GROUP_CLASS "group"
+#define SYSDB_NETGROUP_CLASS "netgroup"
 
 #define SYSDB_NAME "name"
 #define SYSDB_OBJECTCLASS "objectClass"
@@ -75,6 +78,10 @@
 #define SYSDB_CACHE_EXPIRE "dataExpireTimestamp"
 #define SYSDB_INITGR_EXPIRE "initgrExpireTimestamp"
 
+#define SYSDB_NETGROUP_TRIPLE "netgroupTriple"
+#define SYSDB_NETGROUP_MEMBER "memberNetgroup"
+#define SYSDB_DESCRIPTION   "description"
+
 #define SYSDB_CACHEDPWD "cachedPassword"
 
 #define SYSDB_UUID "uniqueID"
@@ -92,6 +99,7 @@
 
 #define SYSDB_UC "objectclass="SYSDB_USER_CLASS
 #define SYSDB_GC "objectclass="SYSDB_GROUP_CLASS
+#define SYSDB_NC "objectclass="SYSDB_NETGROUP_CLASS
 #define SYSDB_MPGC "|("SYSDB_UC")("SYSDB_GC")"
 
 #define SYSDB_PWNAM_FILTER "(&("SYSDB_UC")("SYSDB_NAME"=%s))"
@@ -110,6 +118,8 @@
 
 #define SYSDB_GETCACHED_FILTER "(&"SYSDB_UC")("SYSDB_LAST_LOGIN">=%lu))"
 
+#define SYSDB_NETGR_FILTER "(&("SYSDB_NC")("SYSDB_NAME"=%s))"
+
 #define SYSDB_DEFAULT_ATTRS SYSDB_LAST_UPDATE, \
                             SYSDB_CACHE_EXPIRE, \
                             SYSDB_INITGR_EXPIRE, \
@@ -131,6 +141,10 @@
                            SYSDB_DEFAULT_ATTRS, \
                            NULL}
 
+#define SYSDB_NETGR_ATTRS {SYSDB_NAME, SYSDB_NETGROUP_TRIPLE, \
+                           SYSDB_NETGROUP_MEMBER, SYSDB_DEFAULT_ATTRS, \
+                           NULL}
+
 #define SYSDB_INITGR_ATTR SYSDB_MEMBEROF
 #define SYSDB_INITGR_ATTRS {SYSDB_GIDNUM, \
                             SYSDB_DEFAULT_ATTRS, \
@@ -138,6 +152,7 @@
 
 #define SYSDB_TMPL_USER SYSDB_NAME"=%s,"SYSDB_TMPL_USER_BASE
 #define SYSDB_TMPL_GROUP SYSDB_NAME"=%s,"SYSDB_TMPL_GROUP_BASE
+#define SYSDB_TMPL_NETGROUP SYSDB_NAME"=%s,"SYSDB_TMPL_NETGROUP_BASE
 #define SYSDB_TMPL_CUSTOM_SUBTREE "cn=%s,"SYSDB_TMPL_CUSTOM_BASE
 #define SYSDB_TMPL_CUSTOM SYSDB_NAME"=%s,cn=%s,"SYSDB_TMPL_CUSTOM_BASE
 
@@ -200,6 +215,8 @@ struct ldb_dn *sysdb_user_dn(struct sysdb_ctx *ctx, void *memctx,
                              const char *domain, const char *name);
 struct ldb_dn *sysdb_group_dn(struct sysdb_ctx *ctx, void *memctx,
                               const char *domain, const char *name);
+struct ldb_dn *sysdb_netgroup_dn(struct sysdb_ctx *ctx, void *memctx,
+                                 const char *domain, const char *name);
 errno_t sysdb_group_dn_name(struct sysdb_ctx *ctx, void *memctx,
                             const char *dn_str, char **name);
 struct ldb_dn *sysdb_domain_dn(struct sysdb_ctx *ctx, void *memctx,
@@ -282,6 +299,12 @@ int sysdb_enumgrent(TALLOC_CTX *mem_ctx,
                     struct sss_domain_info *domain,
                     struct ldb_result **res);
 
+errno_t sysdb_getnetgr(TALLOC_CTX *mem_ctx,
+                       struct sysdb_ctx *ctx,
+                       struct sss_domain_info *domain,
+                       const char *netgroup,
+                       struct ldb_result **res);
+
 int sysdb_initgroups(TALLOC_CTX *mem_ctx,
                      struct sysdb_ctx *ctx,
                      struct sss_domain_info *domain,
@@ -295,6 +318,12 @@ int sysdb_get_user_attr(TALLOC_CTX *mem_ctx,
                         const char **attributes,
                         struct ldb_result **res);
 
+int sysdb_get_netgroup_attr(TALLOC_CTX *mem_ctx,
+                            struct sysdb_ctx *ctx,
+                            struct sss_domain_info *domain,
+                            const char *netgrname,
+                            const char **attributes,
+                            struct ldb_result **res);
 
 /* functions that modify the databse
  * they have to be called within a transaction
@@ -336,7 +365,7 @@ int sysdb_search_user_by_uid(TALLOC_CTX *mem_ctx,
                              const char **attrs,
                              struct ldb_message **msg);
 
-/* Search Group (gy gid or name) */
+/* Search Group (by gid or name) */
 int sysdb_search_group_by_name(TALLOC_CTX *mem_ctx,
                                struct sysdb_ctx *ctx,
                                struct sss_domain_info *domain,
@@ -351,6 +380,14 @@ int sysdb_search_group_by_gid(TALLOC_CTX *mem_ctx,
                               const char **attrs,
                               struct ldb_message **msg);
 
+/* Search Netgroup (by name) */
+int sysdb_search_netgroup_by_name(TALLOC_CTX *mem_ctx,
+                                  struct sysdb_ctx *ctx,
+                                  struct sss_domain_info *domain,
+                                  const char *name,
+                                  const char **attrs,
+                                  struct ldb_message **msg);
+
 /* Replace entry attrs */
 int sysdb_set_entry_attr(TALLOC_CTX *mem_ctx,
                          struct sysdb_ctx *ctx,
@@ -374,6 +411,13 @@ int sysdb_set_group_attr(TALLOC_CTX *mem_ctx,
                          struct sysdb_attrs *attrs,
                          int mod_op);
 
+/* Replace netgroup attrs */
+int sysdb_set_netgroup_attr(struct sysdb_ctx *ctx,
+                            struct sss_domain_info *domain,
+                            const char *name,
+                            struct sysdb_attrs *attrs,
+                            int mod_op);
+
 /* Allocate a new id */
 int sysdb_get_new_id(TALLOC_CTX *mem_ctx,
                      struct sysdb_ctx *ctx,
@@ -416,6 +460,18 @@ int sysdb_add_group(TALLOC_CTX *mem_ctx,
                     struct sysdb_attrs *attrs,
                     int cache_timeout);
 
+/* Add netgroup (only basic attrs and w/o checks) */
+int sysdb_add_basic_netgroup(struct sysdb_ctx *ctx,
+                             struct sss_domain_info *domain,
+                             const char *name, const char *description);
+
+int sysdb_add_netgroup(struct sysdb_ctx *ctx,
+                       struct sss_domain_info *domain,
+                       const char *name,
+                       const char *description,
+                       struct sysdb_attrs *attrs,
+                       int cache_timeout);
+
 /* mod_op must be either LDB_FLAG_MOD_ADD or LDB_FLAG_MOD_DELETE */
 int sysdb_mod_group_member(TALLOC_CTX *mem_ctx,
                            struct sysdb_ctx *ctx,
@@ -460,6 +516,44 @@ errno_t sysdb_update_members(struct sysdb_ctx *sysdb,
                              const char **add_groups,
                              const char **del_groups);
 
+errno_t sysdb_add_netgroup_tuple(struct sysdb_ctx *sysdb,
+                                 struct sss_domain_info *domain,
+                                 const char *netgroup,
+                                 const char *hostname,
+                                 const char *username,
+                                 const char *domainname);
+
+errno_t sysdb_remove_netgroup_tuple(struct sysdb_ctx *sysdb,
+                                    struct sss_domain_info *domain,
+                                    const char *netgroup,
+                                    const char *hostname,
+                                    const char *username,
+                                    const char *domainname);
+
+errno_t sysdb_mod_netgroup_tuple(struct sysdb_ctx *sysdb,
+                                 struct sss_domain_info *domain,
+                                 const char *netgroup,
+                                 const char *hostname,
+                                 const char *username,
+                                 const char *domainname,
+                                 int mod_op);
+
+errno_t sysdb_add_netgroup_member(struct sysdb_ctx *sysdb,
+                                  struct sss_domain_info *domain,
+                                  const char *netgroup,
+                                  const char *member_netgroup);
+
+errno_t sysdb_remove_netgroup_member(struct sysdb_ctx *sysdb,
+                                     struct sss_domain_info *domain,
+                                     const char *netgroup,
+                                     const char *member_netgroup);
+
+errno_t sysdb_mod_netgroup_member(struct sysdb_ctx *sysdb,
+                                  struct sss_domain_info *domain,
+                                  const char *netgroup,
+                                  const char *member_netgroup,
+                                  int mod_op);
+
 /* Password caching function.
  * If you are in a transaction ignore sysdb and pass in the handle.
  * If you are not in a transaction pass NULL in handle and provide sysdb,
@@ -554,6 +648,10 @@ int sysdb_delete_group(TALLOC_CTX *mem_ctx,
                        struct sss_domain_info *domain,
                        const char *name, gid_t gid);
 
+int sysdb_delete_netgroup(struct sysdb_ctx *sysdb,
+                          struct sss_domain_info *domain,
+                          const char *name);
+
 errno_t sysdb_attrs_to_list(TALLOC_CTX *memctx,
                             struct sysdb_attrs **attrs,
                             int attr_count,
diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
index f0900286cca1d5a44f392cd32af087603b78008e..2a7e007abb3d8764d094ce4525877aed9871d44f 100644
--- a/src/db/sysdb_ops.c
+++ b/src/db/sysdb_ops.c
@@ -377,6 +377,49 @@ done:
 }
 
 
+/* =Search-Group-by-Name============================================ */
+
+int sysdb_search_netgroup_by_name(TALLOC_CTX *mem_ctx,
+                                  struct sysdb_ctx *ctx,
+                                  struct sss_domain_info *domain,
+                                  const char *name,
+                                  const char **attrs,
+                                  struct ldb_message **msg)
+{
+    TALLOC_CTX *tmpctx;
+    static const char *def_attrs[] = { SYSDB_NAME, NULL };
+    struct ldb_message **msgs = NULL;
+    struct ldb_dn *basedn;
+    size_t msgs_count = 0;
+    int ret;
+
+    tmpctx = talloc_new(mem_ctx);
+    if (!tmpctx) {
+        return ENOMEM;
+    }
+
+    basedn = sysdb_netgroup_dn(ctx, tmpctx, domain->name, name);
+    if (!basedn) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_search_entry(tmpctx, ctx, basedn, LDB_SCOPE_BASE, NULL,
+                             attrs?attrs:def_attrs, &msgs_count, &msgs);
+    if (ret) {
+        goto done;
+    }
+
+    *msg = talloc_steal(mem_ctx, msgs[0]);
+
+done:
+    if (ret) {
+        DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
+    }
+    talloc_zfree(tmpctx);
+    return ret;
+}
+
 /* =Replace-Attributes-On-Entry=========================================== */
 
 int sysdb_set_entry_attr(TALLOC_CTX *mem_ctx,
@@ -463,6 +506,34 @@ int sysdb_set_group_attr(TALLOC_CTX *mem_ctx,
     return sysdb_set_entry_attr(mem_ctx, ctx, dn, attrs, mod_op);
 }
 
+/* =Replace-Attributes-On-Netgroup=========================================== */
+
+int sysdb_set_netgroup_attr(struct sysdb_ctx *ctx,
+                            struct sss_domain_info *domain,
+                            const char *name,
+                            struct sysdb_attrs *attrs,
+                            int mod_op)
+{
+    errno_t ret;
+    struct ldb_dn *dn;
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    dn = sysdb_netgroup_dn(ctx, tmp_ctx, domain->name, name);
+    if (!dn) {
+        goto done;
+    }
+
+    ret = sysdb_set_entry_attr(tmp_ctx, ctx, dn, attrs, mod_op);
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
 
 /* =Get-New-ID============================================================ */
 
@@ -860,7 +931,7 @@ int sysdb_add_basic_group(TALLOC_CTX *mem_ctx,
         return ENOMEM;
     }
 
-    /* user dn */
+    /* group dn */
     msg->dn = sysdb_group_dn(ctx, msg, domain->name, name);
     if (!msg->dn) {
         ERROR_OUT(ret, ENOMEM, done);
@@ -1049,6 +1120,119 @@ fail:
     return ret;
 }
 
+/* =Add-Basic-Netgroup-NO-CHECKS============================================= */
+
+int sysdb_add_basic_netgroup(struct sysdb_ctx *ctx,
+                             struct sss_domain_info *domain,
+                             const char *name, const char *description)
+{
+    struct ldb_message *msg;
+    int ret;
+
+    msg = ldb_msg_new(NULL);
+    if (!msg) {
+        return ENOMEM;
+    }
+
+    /* netgroup dn */
+    msg->dn = sysdb_netgroup_dn(ctx, msg, domain->name, name);
+    if (!msg->dn) {
+        ERROR_OUT(ret, ENOMEM, done);
+    }
+
+    ret = add_string(msg, LDB_FLAG_MOD_ADD,
+                     SYSDB_OBJECTCLASS, SYSDB_NETGROUP_CLASS);
+    if (ret) goto done;
+
+    ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_NAME, name);
+    if (ret) goto done;
+
+    if (description && *description) {
+        ret = add_string(msg, LDB_FLAG_MOD_ADD,
+                         SYSDB_DESCRIPTION, description);
+        if (ret) goto done;
+    }
+
+    /* creation time */
+    ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_CREATE_TIME,
+                    (unsigned long) time(NULL));
+    if (ret) goto done;
+
+    ret = ldb_add(ctx->ldb, msg);
+    ret = sysdb_error_to_errno(ret);
+
+done:
+    if (ret) {
+        DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
+    }
+    talloc_zfree(msg);
+    return ret;
+}
+
+
+/* =Add-Netgroup-Function==================================================== */
+
+int sysdb_add_netgroup(struct sysdb_ctx *ctx,
+                       struct sss_domain_info *domain,
+                       const char *name,
+                       const char *description,
+                       struct sysdb_attrs *attrs,
+                       int cache_timeout)
+{
+    TALLOC_CTX *tmp_ctx;
+    time_t now;
+    int ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    ret = ldb_transaction_start(ctx->ldb);
+    if (ret) {
+        ret = sysdb_error_to_errno(ret);
+        talloc_free(tmp_ctx);
+        return ret;
+    }
+
+    /* FIXME - do we need to check by name if the same exists? */
+
+    /* try to add the netgroup */
+    ret = sysdb_add_basic_netgroup(ctx, domain, name, description);
+    if (ret) goto done;
+
+    if (!attrs) {
+        attrs = sysdb_new_attrs(tmp_ctx);
+        if (!attrs) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    now = time(NULL);
+
+    /* FIXME - Do we need different attributes? */
+    ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
+    if (ret) goto done;
+
+    ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE,
+                                 ((cache_timeout) ?
+                                  (now + cache_timeout) : 0));
+    if (ret) goto done;
+
+    ret = sysdb_set_netgroup_attr(ctx, domain, name, attrs, SYSDB_MOD_REP);
+
+done:
+    if (ret == EOK) {
+        ret = ldb_transaction_commit(ctx->ldb);
+        ret = sysdb_error_to_errno(ret);
+    } else {
+        DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
+        ldb_transaction_cancel(ctx->ldb);
+    }
+    talloc_zfree(tmp_ctx);
+    return ret;
+}
 
 /* =Store-Users-(Native/Legacy)-(replaces-existing-data)================== */
 
@@ -1917,6 +2101,45 @@ fail:
     return ret;
 }
 
+/* =Delete-Netgroup-by-Name============================================== */
+
+int sysdb_delete_netgroup(struct sysdb_ctx *sysdb,
+                          struct sss_domain_info *domain,
+                          const char *name)
+{
+    TALLOC_CTX *tmp_ctx;
+    struct ldb_message *msg;
+    int ret;
+
+    if (!name) return EINVAL;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    ret = sysdb_search_netgroup_by_name(tmp_ctx, sysdb,
+                                        domain, name, NULL, &msg);
+    if (ret) {
+        DEBUG(6, ("sysdb_search_netgroup_by_name failed: %d (%s)\n",
+                   ret, strerror(ret)));
+        goto fail;
+    }
+
+    ret = sysdb_delete_entry(sysdb, msg->dn, false);
+    if (ret) {
+        goto fail;
+    }
+
+    talloc_free(tmp_ctx);
+    return EOK;
+
+fail:
+    DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
 /* ========= Authentication against cached password ============ */
 
 
@@ -2255,3 +2478,124 @@ done:
     talloc_free(tmp_ctx);
     return ret;
 }
+
+errno_t sysdb_add_netgroup_tuple(struct sysdb_ctx *sysdb,
+                                 struct sss_domain_info *domain,
+                                 const char *netgroup,
+                                 const char *hostname,
+                                 const char *username,
+                                 const char *domainname)
+{
+    return sysdb_mod_netgroup_tuple(sysdb, domain, netgroup, hostname,
+                                    username, domainname, SYSDB_MOD_ADD);
+}
+
+errno_t sysdb_remove_netgroup_tuple(struct sysdb_ctx *sysdb,
+                                    struct sss_domain_info *domain,
+                                    const char *netgroup,
+                                    const char *hostname,
+                                    const char *username,
+                                    const char *domainname)
+{
+    return sysdb_mod_netgroup_tuple(sysdb, domain, netgroup, hostname,
+                                    username, domainname, SYSDB_MOD_DEL);
+}
+
+errno_t sysdb_mod_netgroup_tuple(struct sysdb_ctx *sysdb,
+                                 struct sss_domain_info *domain,
+                                 const char *netgroup,
+                                 const char *hostname,
+                                 const char *username,
+                                 const char *domainname,
+                                 int mod_op)
+{
+    errno_t ret;
+    int lret;
+    struct ldb_message *msg;
+    char *triple;
+
+    msg = ldb_msg_new(NULL);
+    if (!msg) {
+        ERROR_OUT(ret, ENOMEM, done);
+    }
+
+    msg->dn = sysdb_netgroup_dn(sysdb, msg, domain->name, netgroup);
+    if (!msg->dn) {
+        ERROR_OUT(ret, ENOMEM, done);
+    }
+
+    triple = talloc_asprintf(msg, "(%s,%s,%s)",
+                             hostname, username, domainname);
+    if (!triple) {
+        ERROR_OUT(ret, ENOMEM, done);
+    }
+
+    ret = add_string(msg, mod_op, SYSDB_NETGROUP_TRIPLE, triple);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    lret = ldb_modify(sysdb->ldb, msg);
+    ret = sysdb_error_to_errno(lret);
+
+done:
+    if (ret) {
+        DEBUG(3, ("Error: %d (%s)\n", ret, strerror(ret)));
+    }
+    talloc_free(msg);
+    return ret;
+}
+
+errno_t sysdb_add_netgroup_member(struct sysdb_ctx *sysdb,
+                                  struct sss_domain_info *domain,
+                                  const char *netgroup,
+                                  const char *member_netgroup)
+{
+    return sysdb_mod_netgroup_member(sysdb, domain, netgroup,
+                                     member_netgroup, SYSDB_MOD_ADD);
+}
+
+errno_t sysdb_remove_netgroup_member(struct sysdb_ctx *sysdb,
+                                  struct sss_domain_info *domain,
+                                  const char *netgroup,
+                                  const char *member_netgroup)
+{
+    return sysdb_mod_netgroup_member(sysdb, domain, netgroup,
+                                     member_netgroup, SYSDB_MOD_DEL);
+}
+
+errno_t sysdb_mod_netgroup_member(struct sysdb_ctx *sysdb,
+                                  struct sss_domain_info *domain,
+                                  const char *netgroup,
+                                  const char *member_netgroup,
+                                  int mod_op)
+{
+    errno_t ret;
+    int lret;
+    struct ldb_message *msg;
+
+    msg = ldb_msg_new(NULL);
+    if (!msg) {
+        ERROR_OUT(ret, ENOMEM, done);
+    }
+
+    msg->dn = sysdb_netgroup_dn(sysdb, msg, domain->name, netgroup);
+    if (!msg->dn) {
+        ERROR_OUT(ret, ENOMEM, done);
+    }
+
+    ret = add_string(msg, mod_op, SYSDB_NETGROUP_MEMBER, member_netgroup);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    lret = ldb_modify(sysdb->ldb, msg);
+    ret = sysdb_error_to_errno(lret);
+
+done:
+    if (ret) {
+        DEBUG(3, ("Error: %d (%s)\n", ret, strerror(ret)));
+    }
+    talloc_free(msg);
+    return ret;
+}
diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
index 6029b99d810835d874d69bc5a60cad6529ce93e1..7b0891ff496acab4b0495da35bf8a0efb34abdce 100644
--- a/src/db/sysdb_search.c
+++ b/src/db/sysdb_search.c
@@ -493,3 +493,90 @@ done:
     talloc_zfree(tmpctx);
     return ret;
 }
+
+errno_t sysdb_getnetgr(TALLOC_CTX *mem_ctx,
+                       struct sysdb_ctx *ctx,
+                       struct sss_domain_info *domain,
+                       const char *netgroup,
+                       struct ldb_result **res)
+{
+    TALLOC_CTX *tmp_ctx;
+    static const char *attrs[] = SYSDB_NETGR_ATTRS;
+    struct ldb_dn *base_dn;
+    struct ldb_result *result;
+    int lret;
+    errno_t ret;
+
+    if (!domain) {
+        return EINVAL;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    base_dn = ldb_dn_new_fmt(tmp_ctx, ctx->ldb,
+                             SYSDB_TMPL_NETGROUP_BASE,
+                             domain->name);
+    if (!base_dn) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    lret = ldb_search(ctx->ldb, tmp_ctx, &result, base_dn,
+                     LDB_SCOPE_SUBTREE, attrs, SYSDB_NETGR_FILTER, netgroup);
+    ret = sysdb_error_to_errno(lret);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    *res = talloc_steal(mem_ctx, result);
+
+done:
+    talloc_zfree(tmp_ctx);
+    return ret;
+}
+
+int sysdb_get_netgroup_attr(TALLOC_CTX *mem_ctx,
+                            struct sysdb_ctx *ctx,
+                            struct sss_domain_info *domain,
+                            const char *netgrname,
+                            const char **attributes,
+                            struct ldb_result **res)
+{
+    TALLOC_CTX *tmpctx;
+    struct ldb_dn *base_dn;
+    struct ldb_result *result;
+    int ret;
+
+    if (!domain) {
+        return EINVAL;
+    }
+
+    tmpctx = talloc_new(mem_ctx);
+    if (!tmpctx) {
+        return ENOMEM;
+    }
+
+    base_dn = ldb_dn_new_fmt(tmpctx, ctx->ldb,
+                             SYSDB_TMPL_NETGROUP_BASE, domain->name);
+    if (!base_dn) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = ldb_search(ctx->ldb, tmpctx, &result, base_dn,
+                     LDB_SCOPE_SUBTREE, attributes,
+                     SYSDB_NETGR_FILTER, netgrname);
+    if (ret) {
+        ret = sysdb_error_to_errno(ret);
+        goto done;
+    }
+
+    *res = talloc_steal(mem_ctx, result);
+done:
+    talloc_zfree(tmpctx);
+    return ret;
+}
+
-- 
1.7.2.3

