r5467: Optimize _samr_query_groupmem with LDAP backend for large domains.
authorVolker Lendecke <vlendec@samba.org>
Sun, 20 Feb 2005 13:47:16 +0000 (13:47 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 15:55:41 +0000 (10:55 -0500)
Could someone else please look at this patch, verifying that I did not break
the ldapsam:trusted = False fallback to the old behaviour? It works fine for
me, but you never know. You're certainly free to review the new code as well :-)

Thanks,

Volker

source/include/passdb.h
source/passdb/pdb_interface.c
source/passdb/pdb_ldap.c
source/rpc_server/srv_samr_nt.c

index 42f38e5b6ac231e51e96e12939399a492a6ae8a6..3c244e7625be58732ffda80d6ead225b1be6ff0d 100644 (file)
@@ -287,6 +287,12 @@ typedef struct pdb_context
                                           GROUP_MAP **rmap, int *num_entries,
                                           BOOL unix_only);
 
+       NTSTATUS (*pdb_enum_group_members)(struct pdb_context *context,
+                                          TALLOC_CTX *mem_ctx,
+                                          const DOM_SID *group,
+                                          uint32 **member_rids,
+                                          int *num_members);
+
        NTSTATUS (*pdb_enum_group_memberships)(struct pdb_context *context,
                                               const char *username,
                                               gid_t primary_gid,
@@ -385,6 +391,12 @@ typedef struct pdb_methods
                                       GROUP_MAP **rmap, int *num_entries,
                                       BOOL unix_only);
 
+       NTSTATUS (*enum_group_members)(struct pdb_methods *methods,
+                                      TALLOC_CTX *mem_ctx,
+                                      const DOM_SID *group,
+                                      uint32 **member_rids,
+                                      int *num_members);
+
        NTSTATUS (*enum_group_memberships)(struct pdb_methods *methods,
                                           const char *username,
                                           gid_t primary_gid,
index 382c028b0c6b12ee723f1a287f988f1dd536ce99..36a575214b941efeaeb9f8f5171f6bcf17ad15b6 100644 (file)
@@ -453,6 +453,25 @@ static NTSTATUS context_enum_group_mapping(struct pdb_context *context,
                                                        num_entries, unix_only);
 }
 
+static NTSTATUS context_enum_group_members(struct pdb_context *context,
+                                          TALLOC_CTX *mem_ctx,
+                                          const DOM_SID *group,
+                                          uint32 **member_rids,
+                                          int *num_members)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+       if ((!context) || (!context->pdb_methods)) {
+               DEBUG(0, ("invalid pdb_context specified!\n"));
+               return ret;
+       }
+
+       return context->pdb_methods->enum_group_members(context->pdb_methods,
+                                                       mem_ctx, group,
+                                                       member_rids,
+                                                       num_members);
+}
+
 static NTSTATUS context_enum_group_memberships(struct pdb_context *context,
                                               const char *username,
                                               gid_t primary_gid,
@@ -737,6 +756,7 @@ static NTSTATUS make_pdb_context(struct pdb_context **context)
        (*context)->pdb_update_group_mapping_entry = context_update_group_mapping_entry;
        (*context)->pdb_delete_group_mapping_entry = context_delete_group_mapping_entry;
        (*context)->pdb_enum_group_mapping = context_enum_group_mapping;
+       (*context)->pdb_enum_group_members = context_enum_group_members;
        (*context)->pdb_enum_group_memberships = context_enum_group_memberships;
 
        (*context)->pdb_find_alias = context_find_alias;
@@ -1058,6 +1078,21 @@ BOOL pdb_enum_group_mapping(enum SID_NAME_USE sid_name_use, GROUP_MAP **rmap,
                                                      rmap, num_entries, unix_only));
 }
 
+NTSTATUS pdb_enum_group_members(TALLOC_CTX *mem_ctx,
+                               const DOM_SID *sid,
+                               uint32 **member_rids,
+                               int *num_members)
+{
+       struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+       if (!pdb_context) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       return pdb_context->pdb_enum_group_members(pdb_context, mem_ctx, sid, 
+                                                  member_rids, num_members);
+}
+
 NTSTATUS pdb_enum_group_memberships(const char *username, gid_t primary_gid,
                                    DOM_SID **sids, gid_t **gids,
                                    int *num_groups)
@@ -1259,6 +1294,116 @@ static void pdb_default_endsampwent(struct pdb_methods *methods)
        return; /* NT_STATUS_NOT_IMPLEMENTED; */
 }
 
+static void add_uid_to_array_unique(TALLOC_CTX *mem_ctx,
+                                   uid_t uid, uid_t **uids, int *num)
+{
+       int i;
+
+       for (i=0; i<*num; i++) {
+               if ((*uids)[i] == uid)
+                       return;
+       }
+       
+       *uids = TALLOC_REALLOC_ARRAY(mem_ctx, *uids, uid_t, *num+1);
+
+       if (*uids == NULL)
+               return;
+
+       (*uids)[*num] = uid;
+       *num += 1;
+}
+
+static BOOL get_memberuids(TALLOC_CTX *mem_ctx, gid_t gid, uid_t **uids,
+                          int *num)
+{
+       struct group *grp;
+       char **gr;
+       struct sys_pwent *userlist, *user;
+       *uids = NULL;
+       *num = 0;
+
+       /* We only look at our own sam, so don't care about imported stuff */
+
+       winbind_off();
+
+       if ((grp = getgrgid(gid)) == NULL) {
+               winbind_on();
+               return False;
+       }
+
+       /* Primary group members */
+
+       userlist = getpwent_list();
+
+       for (user = userlist; user != NULL; user = user->next) {
+               if (user->pw_gid != gid)
+                       continue;
+               add_uid_to_array_unique(mem_ctx, user->pw_uid, uids, num);
+       }
+
+       pwent_free(userlist);
+
+       /* Secondary group members */
+
+       for (gr = grp->gr_mem; (*gr != NULL) && ((*gr)[0] != '\0'); gr += 1) {
+               struct passwd *pw = getpwnam(*gr);
+
+               if (pw == NULL)
+                       continue;
+               add_uid_to_array_unique(mem_ctx, pw->pw_uid, uids, num);
+       }
+
+       winbind_on();
+
+       return True;
+}
+
+NTSTATUS pdb_default_enum_group_members(struct pdb_methods *methods,
+                                       TALLOC_CTX *mem_ctx,
+                                       const DOM_SID *group,
+                                       uint32 **member_rids,
+                                       int *num_members)
+{
+       gid_t gid;
+       uid_t *uids;
+       int i, num_uids;
+
+       *member_rids = NULL;
+       *num_members = 0;
+
+       if (!NT_STATUS_IS_OK(sid_to_gid(group, &gid)))
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       if(!get_memberuids(mem_ctx, gid, &uids, &num_uids))
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       if (num_uids == 0)
+               return NT_STATUS_OK;
+
+       *member_rids = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_uids);
+
+       for (i=0; i<num_uids; i++) {
+               DOM_SID sid;
+
+               if (!NT_STATUS_IS_OK(uid_to_sid(&sid, uids[i]))) {
+                       DEBUG(1, ("Could not map member uid to SID\n"));
+                       continue;
+               }
+
+               if (!sid_check_is_in_our_domain(&sid)) {
+                       DEBUG(1, ("Inconsistent SAM -- group member uid not "
+                                 "in our domain\n"));
+                       continue;
+               }
+
+               sid_peek_rid(&sid, &(*member_rids)[*num_members]);
+               *num_members += 1;
+       }
+
+       return NT_STATUS_OK;
+}
+
 NTSTATUS make_pdb_methods(TALLOC_CTX *mem_ctx, PDB_METHODS **methods) 
 {
        *methods = TALLOC_P(mem_ctx, struct pdb_methods);
@@ -1285,6 +1430,7 @@ NTSTATUS make_pdb_methods(TALLOC_CTX *mem_ctx, PDB_METHODS **methods)
        (*methods)->update_group_mapping_entry = pdb_default_update_group_mapping_entry;
        (*methods)->delete_group_mapping_entry = pdb_default_delete_group_mapping_entry;
        (*methods)->enum_group_mapping = pdb_default_enum_group_mapping;
+       (*methods)->enum_group_members = pdb_default_enum_group_members;
        (*methods)->enum_group_memberships = pdb_default_enum_group_memberships;
        (*methods)->find_alias = pdb_default_find_alias;
        (*methods)->create_alias = pdb_default_create_alias;
index 207b587685d4cfc14a6a245f2ac397e7579d7ff6..e8facf1c2ed77c7facc70c44a9bc17cf2c5e5b28 100644 (file)
@@ -2165,6 +2165,213 @@ static NTSTATUS ldapsam_getgrnam(struct pdb_methods *methods, GROUP_MAP *map,
        return ldapsam_getgroup(methods, filter, map);
 }
 
+static void add_rid_to_array_unique(TALLOC_CTX *mem_ctx,
+                                   uint32 rid, uint32 **rids, int *num)
+{
+       int i;
+
+       for (i=0; i<*num; i++) {
+               if ((*rids)[i] == rid)
+                       return;
+       }
+       
+       *rids = TALLOC_REALLOC_ARRAY(mem_ctx, *rids, uint32, *num+1);
+
+       if (*rids == NULL)
+               return;
+
+       (*rids)[*num] = rid;
+       *num += 1;
+}
+
+static NTSTATUS ldapsam_enum_group_members(struct pdb_methods *methods,
+                                          TALLOC_CTX *mem_ctx,
+                                          const DOM_SID *group,
+                                          uint32 **member_rids,
+                                          int *num_members)
+{
+       struct ldapsam_privates *ldap_state =
+               (struct ldapsam_privates *)methods->private_data;
+       struct smbldap_state *conn = ldap_state->smbldap_state;
+       pstring filter;
+       int rc, count;
+       LDAPMessage *msg = NULL;
+       LDAPMessage *entry;
+       char **values = NULL;
+       char **memberuid;
+       char *sid_filter = NULL;
+       char *tmp;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       if (!lp_parm_bool(-1, "ldapsam", "trusted", False))
+               return pdb_default_enum_group_members(methods, mem_ctx, group,
+                                                     member_rids,
+                                                     num_members);
+
+       *member_rids = NULL;
+       *num_members = 0;
+
+       pstr_sprintf(filter,
+                    "(&(objectClass=sambaSamAccount)"
+                    "(sambaPrimaryGroupSid=%s))",
+                    sid_string_static(group));
+
+       {
+               const char *attrs[] = { "sambaSID", NULL };
+               rc = smbldap_search(conn, lp_ldap_user_suffix(),
+                                   LDAP_SCOPE_SUBTREE, filter, attrs, 0,
+                                   &msg);
+       }
+
+       if (rc != LDAP_SUCCESS)
+               goto done;
+
+       for (entry = ldap_first_entry(conn->ldap_struct, msg);
+            entry != NULL;
+            entry = ldap_next_entry(conn->ldap_struct, entry))
+       {
+               fstring str;
+               DOM_SID sid;
+               uint32 rid;
+
+               if (!smbldap_get_single_attribute(conn->ldap_struct,
+                                                 entry, "sambaSID",
+                                                 str, sizeof(str)-1))
+                       continue;
+
+               if (!string_to_sid(&sid, str))
+                       goto done;
+
+               if (!sid_check_is_in_our_domain(&sid)) {
+                       DEBUG(1, ("Inconsistent SAM -- group member uid not "
+                                 "in our domain\n"));
+                       continue;
+               }
+
+               sid_peek_rid(&sid, &rid);
+
+               add_rid_to_array_unique(mem_ctx, rid, member_rids,
+                                       num_members);
+       }
+
+       if (msg != NULL)
+               ldap_msgfree(msg);
+
+       pstr_sprintf(filter,
+                    "(&(objectClass=sambaGroupMapping)"
+                    "(objectClass=posixGroup)"
+                    "(sambaSID=%s))",
+                    sid_string_static(group));
+
+       {
+               const char *attrs[] = { "memberUid", NULL };
+               rc = smbldap_search(conn, lp_ldap_user_suffix(),
+                                   LDAP_SCOPE_SUBTREE, filter, attrs, 0,
+                                   &msg);
+       }
+
+       if (rc != LDAP_SUCCESS)
+               goto done;
+
+       count = ldap_count_entries(conn->ldap_struct, msg);
+
+       if (count > 1) {
+               DEBUG(1, ("Found more than one groupmap entry for %s\n",
+                         sid_string_static(group)));
+               goto done;
+       }
+
+       if (count == 0) {
+               result = NT_STATUS_OK;
+               goto done;
+       }
+
+       entry = ldap_first_entry(conn->ldap_struct, msg);
+       if (entry == NULL)
+               goto done;
+
+       values = ldap_get_values(conn->ldap_struct, msg, "memberUid");
+       if (values == NULL) {
+               result = NT_STATUS_OK;
+               goto done;
+       }
+
+       sid_filter = strdup("(&(objectClass=sambaSamAccount)(|");
+       if (sid_filter == NULL) {
+               result = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       for (memberuid = values; *memberuid != NULL; memberuid += 1) {
+               tmp = sid_filter;
+               asprintf(&sid_filter, "%s(uid=%s)", tmp, *memberuid);
+               free(tmp);
+               if (sid_filter == NULL) {
+                       result = NT_STATUS_NO_MEMORY;
+                       goto done;
+               }
+       }
+
+       tmp = sid_filter;
+       asprintf(&sid_filter, "%s))", sid_filter);
+       free(tmp);
+       if (sid_filter == NULL) {
+               result = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       {
+               const char *attrs[] = { "sambaSID", NULL };
+               rc = smbldap_search(conn, lp_ldap_user_suffix(),
+                                   LDAP_SCOPE_SUBTREE, sid_filter, attrs, 0,
+                                   &msg);
+       }
+
+       if (rc != LDAP_SUCCESS)
+               goto done;
+
+       for (entry = ldap_first_entry(conn->ldap_struct, msg);
+            entry != NULL;
+            entry = ldap_next_entry(conn->ldap_struct, entry))
+       {
+               fstring str;
+               DOM_SID sid;
+               uint32 rid;
+
+               if (!smbldap_get_single_attribute(conn->ldap_struct,
+                                                 entry, "sambaSID",
+                                                 str, sizeof(str)-1))
+                       continue;
+
+               if (!string_to_sid(&sid, str))
+                       goto done;
+
+               if (!sid_check_is_in_our_domain(&sid)) {
+                       DEBUG(1, ("Inconsistent SAM -- group member uid not "
+                                 "in our domain\n"));
+                       continue;
+               }
+
+               sid_peek_rid(&sid, &rid);
+
+               add_rid_to_array_unique(mem_ctx, rid, member_rids,
+                                       num_members);
+       }
+
+       result = NT_STATUS_OK;
+       
+ done:
+       SAFE_FREE(sid_filter);
+
+       if (values != NULL)
+               ldap_value_free(values);
+
+       if (msg != NULL)
+               ldap_msgfree(msg);
+
+       return result;
+}
+
 static NTSTATUS ldapsam_enum_group_memberships(struct pdb_methods *methods,
                                               const char *username,
                                               gid_t primary_gid,
@@ -2936,6 +3143,7 @@ static NTSTATUS pdb_init_ldapsam_common(PDB_CONTEXT *pdb_context, PDB_METHODS **
        (*pdb_method)->update_group_mapping_entry = ldapsam_update_group_mapping_entry;
        (*pdb_method)->delete_group_mapping_entry = ldapsam_delete_group_mapping_entry;
        (*pdb_method)->enum_group_mapping = ldapsam_enum_group_mapping;
+       (*pdb_method)->enum_group_members = ldapsam_enum_group_members;
        (*pdb_method)->enum_group_memberships = ldapsam_enum_group_memberships;
 
        /* TODO: Setup private data and free */
index ec85981cbef87be05193728b2bf05b269e32755a..7a436e23e94722f43190e4bc287ad191a35a1116 100644 (file)
@@ -3434,18 +3434,17 @@ static BOOL get_memberuids(gid_t gid, uid_t **uids, int *num)
 
 NTSTATUS _samr_query_groupmem(pipes_struct *p, SAMR_Q_QUERY_GROUPMEM *q_u, SAMR_R_QUERY_GROUPMEM *r_u)
 {
-       int final_num_rids, i;
        DOM_SID group_sid;
        fstring group_sid_str;
-       uid_t *uids;
-       int num;
-       gid_t gid;
+       int i, num_members;
 
        uint32 *rid=NULL;
        uint32 *attr=NULL;
 
        uint32 acc_granted;
 
+       NTSTATUS result;
+
        /* find the policy handle.  open a policy on it. */
        if (!get_lsa_policy_samr_sid(p, &q_u->group_pol, &group_sid, &acc_granted)) 
                return NT_STATUS_INVALID_HANDLE;
@@ -3464,46 +3463,23 @@ NTSTATUS _samr_query_groupmem(pipes_struct *p, SAMR_Q_QUERY_GROUPMEM *q_u, SAMR_
 
        DEBUG(10, ("lookup on Domain SID\n"));
 
-       if (!NT_STATUS_IS_OK(sid_to_gid(&group_sid, &gid)))
-               return NT_STATUS_NO_SUCH_GROUP;
+       become_root();
+       result = pdb_enum_group_members(p->mem_ctx, &group_sid,
+                                       &rid, &num_members);
+       unbecome_root();
 
-       if(!get_memberuids(gid, &uids, &num))
-               return NT_STATUS_NO_SUCH_GROUP;
+       if (!NT_STATUS_IS_OK(result))
+               return result;
 
-       rid=TALLOC_ZERO_ARRAY(p->mem_ctx, uint32, num);
-       attr=TALLOC_ZERO_ARRAY(p->mem_ctx, uint32, num);
+       attr=TALLOC_ZERO_ARRAY(p->mem_ctx, uint32, num_members);
        
-       if (num!=0 && (rid==NULL || attr==NULL))
+       if ((num_members!=0) && (rid==NULL))
                return NT_STATUS_NO_MEMORY;
        
-       final_num_rids = 0;
-               
-       for (i=0; i<num; i++) {
-               DOM_SID sid;
-
-               if (!NT_STATUS_IS_OK(uid_to_sid(&sid, uids[i]))) {
-                       DEBUG(1, ("Could not map member uid to SID\n"));
-                       continue;
-               }
-
-               if (!sid_check_is_in_our_domain(&sid)) {
-                       DEBUG(1, ("Inconsistent SAM -- group member uid not "
-                                 "in our domain\n"));
-                       continue;
-               }
-
-               sid_peek_rid(&sid, &rid[final_num_rids]);
-
-               /* Hmm. In a trace I got the constant 7 here from NT. */
-               attr[final_num_rids] = SID_NAME_USER;
-
-               final_num_rids += 1;
-       }
-
-       SAFE_FREE(uids);
+       for (i=0; i<num_members; i++)
+               attr[i] = SID_NAME_USER;
 
-       init_samr_r_query_groupmem(r_u, final_num_rids, rid, attr,
-                                  NT_STATUS_OK);
+       init_samr_r_query_groupmem(r_u, num_members, rid, attr, NT_STATUS_OK);
 
        return NT_STATUS_OK;
 }